Add an accessor for the loader's corruption reason
[platform/upstream/dbus.git] / dbus / dbus-message.c
index 91f6ebe..272592e 100644 (file)
@@ -18,7 +18,7 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  *
  */
 
@@ -142,7 +142,7 @@ _dbus_message_byteswap (DBusMessage *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()).
+ * data once a message is locked (with dbus_message_lock()).
  *
  * @param message the message.
  * @param header return location for message header data.
@@ -163,16 +163,19 @@ _dbus_message_get_network_data (DBusMessage          *message,
  * Sets the serial number of a message.
  * This can only be done once on a message.
  *
+ * DBusConnection will automatically set the serial to an appropriate value 
+ * when the message is sent; this function is only needed when encapsulating 
+ * messages in another protocol, or otherwise bypassing DBusConnection.
+ *
  * @param message the message
  * @param serial the serial
  */
-void
-_dbus_message_set_serial (DBusMessage   *message,
-                          dbus_uint32_t  serial)
+void 
+dbus_message_set_serial (DBusMessage   *message,
+                         dbus_uint32_t  serial)
 {
-  _dbus_assert (message != NULL);
-  _dbus_assert (!message->locked);
-  _dbus_assert (dbus_message_get_serial (message) == 0);
+  _dbus_return_if_fail (message != NULL);
+  _dbus_return_if_fail (!message->locked);
 
   _dbus_header_set_serial (&message->header, serial);
 }
@@ -277,12 +280,13 @@ _dbus_message_remove_size_counter (DBusMessage  *message,
  * reference to a message in the outgoing queue and change it
  * underneath us. Messages are locked when they enter the outgoing
  * queue (dbus_connection_send_message()), and the library complains
- * if the message is modified while locked.
+ * if the message is modified while locked. This function may also 
+ * called externally, for applications wrapping D-Bus in another protocol.
  *
  * @param message the message to lock.
  */
 void
-_dbus_message_lock (DBusMessage  *message)
+dbus_message_lock (DBusMessage  *message)
 {
   if (!message->locked)
     {
@@ -1563,8 +1567,10 @@ dbus_message_append_args_valist (DBusMessage *message,
               if (!dbus_message_iter_append_fixed_array (&array,
                                                          element_type,
                                                          value,
-                                                         n_elements))
+                                                         n_elements)) {
+                dbus_message_iter_abandon_container (&iter, &array);
                 goto failed;
+              }
             }
           else if (element_type == DBUS_TYPE_STRING ||
                    element_type == DBUS_TYPE_SIGNATURE ||
@@ -1585,8 +1591,10 @@ dbus_message_append_args_valist (DBusMessage *message,
                 {
                   if (!dbus_message_iter_append_basic (&array,
                                                        element_type,
-                                                       &value[i]))
+                                                       &value[i])) {
+                    dbus_message_iter_abandon_container (&iter, &array);
                     goto failed;
+                  }
                   ++i;
                 }
             }
@@ -1726,7 +1734,7 @@ _dbus_message_iter_init_common (DBusMessage         *message,
  *
  * The easiest way to iterate is like this: 
  * @code
- * dbus_message_iter_init (&iter);
+ * dbus_message_iter_init (message, &iter);
  * while ((current_type = dbus_message_iter_get_arg_type (&iter)) != DBUS_TYPE_INVALID)
  *   dbus_message_iter_next (&iter);
  * @endcode
@@ -2196,6 +2204,35 @@ _dbus_message_iter_close_signature (DBusMessageRealIter *real)
   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)
@@ -2424,6 +2461,32 @@ dbus_message_iter_close_container (DBusMessageIter *iter,
 }
 
 /**
+ * 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
@@ -3545,6 +3608,9 @@ load_message (DBusMessageLoader *loader,
 
   _dbus_string_delete (&loader->data, 0, header_len + body_len);
 
+  /* don't waste more than 2k of memory */
+  _dbus_string_compact (&loader->data, 2048);
+
   _dbus_assert (_dbus_string_get_length (&message->header.data) == header_len);
   _dbus_assert (_dbus_string_get_length (&message->body) == body_len);
 
@@ -3719,6 +3785,21 @@ _dbus_message_loader_get_is_corrupted (DBusMessageLoader *loader)
 }
 
 /**
+ * Checks what kind of bad data confused the loader.
+ *
+ * @param loader the loader
+ * @returns why the loader is hosed, or DBUS_VALID if it isn't.
+ */
+DBusValidity
+_dbus_message_loader_get_corruption_reason (DBusMessageLoader *loader)
+{
+  _dbus_assert ((loader->corrupted && loader->corruption_reason != DBUS_VALID) ||
+                (!loader->corrupted && loader->corruption_reason == DBUS_VALID));
+
+  return loader->corruption_reason;
+}
+
+/**
  * Sets the maximum size message we allow.
  *
  * @param loader the loader
@@ -3934,14 +4015,21 @@ dbus_message_marshal (DBusMessage  *msg,
                       int          *len_p)
 {
   DBusString tmp;
+  dbus_bool_t was_locked;
 
   _dbus_return_val_if_fail (msg != NULL, FALSE);
   _dbus_return_val_if_fail (marshalled_data_p != NULL, FALSE);
   _dbus_return_val_if_fail (len_p != NULL, FALSE);
-
+  
   if (!_dbus_string_init (&tmp))
     return FALSE;
 
+  /* Ensure the message is locked, to ensure the length header is filled in. */
+  was_locked = msg->locked;
+
+  if (!was_locked)
+    dbus_message_lock (msg);
+
   if (!_dbus_string_copy (&(msg->header.data), 0, &tmp, 0))
     goto fail;
 
@@ -3956,10 +4044,18 @@ dbus_message_marshal (DBusMessage  *msg,
     goto fail;
 
   _dbus_string_free (&tmp);
+
+  if (!was_locked)
+    msg->locked = FALSE;
+
   return TRUE;
 
  fail:
   _dbus_string_free (&tmp);
+
+  if (!was_locked)
+    msg->locked = FALSE;
+
   return FALSE;
 }
 
@@ -4010,7 +4106,8 @@ dbus_message_demarshal (const char *str,
   return msg;
 
  fail_corrupt:
-  dbus_set_error (error, DBUS_ERROR_INVALID_ARGS, "Message is corrupted");
+  dbus_set_error (error, DBUS_ERROR_INVALID_ARGS, "Message is corrupted (%s)",
+                  _dbus_validity_to_error_message (loader->corruption_reason));
   _dbus_message_loader_unref (loader);
   return NULL;
 
@@ -4020,6 +4117,57 @@ dbus_message_demarshal (const char *str,
   return NULL;
 }
 
+/**
+ * Returns the number of bytes required to be in the buffer to demarshal a
+ * D-Bus message.
+ *
+ * Generally, this function is only useful for encapsulating D-Bus messages in
+ * a different protocol.
+ *
+ * @param str data to be marshalled
+ * @param len the length of str
+ * @param error the location to save errors to
+ * @returns -1 if there was no valid data to be demarshalled, 0 if there wasn't enough data to determine how much should be demarshalled. Otherwise returns the number of bytes to be demarshalled
+ * 
+ */
+int 
+dbus_message_demarshal_bytes_needed(const char *buf, 
+                                    int         len)
+{
+  DBusString str;
+  int byte_order, fields_array_len, header_len, body_len;
+  DBusValidity validity = DBUS_VALID;
+  int have_message;
+
+  if (!buf || len < DBUS_MINIMUM_HEADER_SIZE)
+    return 0;
+
+  if (len > DBUS_MAXIMUM_MESSAGE_LENGTH)
+    len = DBUS_MAXIMUM_MESSAGE_LENGTH;
+  _dbus_string_init_const_len (&str, buf, len);
+  
+  validity = DBUS_VALID;
+  have_message
+    = _dbus_header_have_message_untrusted(DBUS_MAXIMUM_MESSAGE_LENGTH,
+                                          &validity, &byte_order,
+                                          &fields_array_len,
+                                          &header_len,
+                                          &body_len,
+                                          &str, 0,
+                                          len);
+  _dbus_string_free (&str);
+
+  if (validity == DBUS_VALID)
+    {
+      _dbus_assert(have_message);
+      return header_len + body_len;
+    }
+  else
+    {
+      return -1; /* broken! */
+    }
+}
+
 /** @} */
 
 /* tests in dbus-message-util.c */