summaryrefslogtreecommitdiffstats
path: root/src/3rdparty/libdbus/dbus
diff options
context:
space:
mode:
Diffstat (limited to 'src/3rdparty/libdbus/dbus')
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-address.c654
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-address.h87
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-arch-deps.h5
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-arch-deps.h.in65
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-auth.c2970
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-auth.h104
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-backtrace-win.c213
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-bus.c1605
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-bus.h97
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-connection-internal.h161
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-connection.c6451
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-connection.h531
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-credentials.c835
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-credentials.h126
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-dataslot.c466
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-dataslot.h100
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-errors.c437
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-errors.h92
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-file-unix.c459
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-file-win.c408
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-file.c29
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-file.h67
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-hash.c1595
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-hash.h226
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-init-win.cpp54
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-init-win.h17
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-internals.c1194
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-internals.h528
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-keyring.c1155
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-keyring.h54
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-list.c819
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-list.h132
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-macros-internal.h54
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-macros.h237
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-marshal-basic.c1993
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-marshal-basic.h233
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-marshal-byteswap.c250
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-marshal-byteswap.h40
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-marshal-header.c1576
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-marshal-header.h179
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-marshal-recursive.c2754
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-marshal-recursive.h203
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-marshal-validate.c1296
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-marshal-validate.h213
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-memory.c952
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-memory.h74
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-mempool.c469
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-mempool.h56
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-message-internal.h150
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-message-private.h148
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-message.c5574
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-message.h401
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-misc.c226
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-misc.h60
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-nonce.c531
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-nonce.h69
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-object-tree.c2333
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-object-tree.h65
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-pending-call-internal.h73
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-pending-call.c872
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-pending-call.h100
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-pipe-unix.c85
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-pipe-win.c92
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-pipe.c87
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-pipe.h66
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-protocol.h488
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-resources.c342
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-resources.h69
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-server-debug-pipe.c433
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-server-debug-pipe.h49
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-server-protected.h186
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-server-socket.c884
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-server-socket.h61
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-server-unix.c145
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-server-win.c97
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-server-win.h38
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-server.c1197
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-server.h127
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-sha.c513
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-sha.h58
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-shared.h138
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-signature.c417
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-signature.h97
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-sockets-win.h55
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-string-private.h135
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-string.c2816
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-string.h454
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-syntax.c311
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-syntax.h60
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-sysdeps-pthread.c322
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-sysdeps-thread-win.c339
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-sysdeps-unix.c5273
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-sysdeps-unix.h169
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-sysdeps-win.c4521
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-sysdeps-win.h125
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-sysdeps.c1021
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-sysdeps.h775
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-test-tap.h66
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-test.h57
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-threads-internal.h168
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-threads.c452
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-threads.h191
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-timeout.c519
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-timeout.h84
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-transport-protected.h148
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-transport-socket.c1645
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-transport-socket.h54
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-transport-unix.c317
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-transport-unix.h37
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-transport-win.c60
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-transport-win.h35
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-transport.c1633
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-transport.h121
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-types.h179
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-userdb.c735
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-userdb.h124
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-uuidgen.c132
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-uuidgen.h49
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-valgrind-internal.h69
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-watch.c764
-rw-r--r--src/3rdparty/libdbus/dbus/dbus-watch.h115
-rw-r--r--src/3rdparty/libdbus/dbus/dbus.h106
122 files changed, 72822 insertions, 0 deletions
diff --git a/src/3rdparty/libdbus/dbus/dbus-address.c b/src/3rdparty/libdbus/dbus/dbus-address.c
new file mode 100644
index 00000000..1093d7d1
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-address.c
@@ -0,0 +1,654 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-address.c Server address parser.
+ *
+ * Copyright (C) 2003 CodeFactory AB
+ * Copyright (C) 2004-2007 Red Hat, Inc.
+ * Copyright (C) 2007 Ralf Habacker
+ * Copyright (C) 2013 Chengwei Yang / Intel
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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-address.h"
+#include "dbus-internals.h"
+#include "dbus-list.h"
+#include "dbus-string.h"
+#include "dbus-protocol.h"
+#include <dbus/dbus-test-tap.h>
+
+/**
+ * @defgroup DBusAddressInternals Address parsing
+ * @ingroup DBusInternals
+ * @brief Implementation of parsing addresses of D-Bus servers.
+ *
+ * @{
+ */
+
+/**
+ * Internals of DBusAddressEntry
+ */
+struct DBusAddressEntry
+{
+ DBusString method; /**< The address type (unix, tcp, etc.) */
+
+ DBusList *keys; /**< List of keys */
+ DBusList *values; /**< List of values */
+};
+
+
+/**
+ *
+ * Sets #DBUS_ERROR_BAD_ADDRESS.
+ * If address_problem_type and address_problem_field are not #NULL,
+ * sets an error message about how the field is no good. Otherwise, sets
+ * address_problem_other as the error message.
+ *
+ * @param error the error to set
+ * @param address_problem_type the address type of the bad address or #NULL
+ * @param address_problem_field the missing field of the bad address or #NULL
+ * @param address_problem_other any other error message or #NULL
+ */
+void
+_dbus_set_bad_address (DBusError *error,
+ const char *address_problem_type,
+ const char *address_problem_field,
+ const char *address_problem_other)
+{
+ if (address_problem_type != NULL)
+ dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS,
+ "Server address of type %s was missing argument %s",
+ address_problem_type, address_problem_field);
+ else
+ dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS,
+ "Could not parse server address: %s",
+ address_problem_other);
+}
+
+/**
+ * #TRUE if the byte need not be escaped when found in a dbus address.
+ * All other bytes are required to be escaped in a valid address.
+ */
+#define _DBUS_ADDRESS_OPTIONALLY_ESCAPED_BYTE(b) \
+ (((b) >= 'a' && (b) <= 'z') || \
+ ((b) >= 'A' && (b) <= 'Z') || \
+ ((b) >= '0' && (b) <= '9') || \
+ (b) == '-' || \
+ (b) == '_' || \
+ (b) == '/' || \
+ (b) == '\\' || \
+ (b) == '*' || \
+ (b) == '.')
+
+/**
+ * Appends an escaped version of one string to another string,
+ * using the D-Bus address escaping mechanism
+ *
+ * @param escaped the string to append to
+ * @param unescaped the string to escape
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_address_append_escaped (DBusString *escaped,
+ const DBusString *unescaped)
+{
+ const unsigned char *p;
+ const unsigned char *end;
+ dbus_bool_t ret;
+ int orig_len;
+
+ ret = FALSE;
+
+ orig_len = _dbus_string_get_length (escaped);
+ p = _dbus_string_get_const_udata (unescaped);
+ end = p + _dbus_string_get_length (unescaped);
+ while (p != end)
+ {
+ if (_DBUS_ADDRESS_OPTIONALLY_ESCAPED_BYTE (*p))
+ {
+ if (!_dbus_string_append_byte (escaped, *p))
+ goto out;
+ }
+ else
+ {
+ if (!_dbus_string_append_byte (escaped, '%'))
+ goto out;
+ if (!_dbus_string_append_byte_as_hex (escaped, *p))
+ goto out;
+ }
+
+ ++p;
+ }
+
+ ret = TRUE;
+
+ out:
+ if (!ret)
+ _dbus_string_set_length (escaped, orig_len);
+ return ret;
+}
+
+/** @} */ /* End of internals */
+
+static void
+dbus_address_entry_free (DBusAddressEntry *entry)
+{
+ DBusList *link;
+
+ _dbus_string_free (&entry->method);
+
+ link = _dbus_list_get_first_link (&entry->keys);
+ while (link != NULL)
+ {
+ _dbus_string_free (link->data);
+ dbus_free (link->data);
+
+ link = _dbus_list_get_next_link (&entry->keys, link);
+ }
+ _dbus_list_clear (&entry->keys);
+
+ link = _dbus_list_get_first_link (&entry->values);
+ while (link != NULL)
+ {
+ _dbus_string_free (link->data);
+ dbus_free (link->data);
+
+ link = _dbus_list_get_next_link (&entry->values, link);
+ }
+ _dbus_list_clear (&entry->values);
+
+ dbus_free (entry);
+}
+
+/**
+ * @defgroup DBusAddress Address parsing
+ * @ingroup DBus
+ * @brief Parsing addresses of D-Bus servers.
+ *
+ * @{
+ */
+
+/**
+ * Frees a #NULL-terminated array of address entries.
+ *
+ * @param entries the array.
+ */
+void
+dbus_address_entries_free (DBusAddressEntry **entries)
+{
+ int i;
+
+ for (i = 0; entries[i] != NULL; i++)
+ dbus_address_entry_free (entries[i]);
+ dbus_free (entries);
+}
+
+static DBusAddressEntry *
+create_entry (void)
+{
+ DBusAddressEntry *entry;
+
+ entry = dbus_new0 (DBusAddressEntry, 1);
+
+ if (entry == NULL)
+ return NULL;
+
+ if (!_dbus_string_init (&entry->method))
+ {
+ dbus_free (entry);
+ return NULL;
+ }
+
+ return entry;
+}
+
+/**
+ * Returns the method string of an address entry. For example, given
+ * the address entry "tcp:host=example.com" it would return the string
+ * "tcp"
+ *
+ * @param entry the entry.
+ * @returns a string describing the method. This string
+ * must not be freed.
+ */
+const char *
+dbus_address_entry_get_method (DBusAddressEntry *entry)
+{
+ return _dbus_string_get_const_data (&entry->method);
+}
+
+/**
+ * Returns a value from a key of an entry. For example,
+ * given the address "tcp:host=example.com,port=8073" if you asked
+ * for the key "host" you would get the value "example.com"
+ *
+ * The returned value is already unescaped.
+ *
+ * @param entry the entry.
+ * @param key the key.
+ * @returns the key value. This string must not be freed.
+ */
+const char *
+dbus_address_entry_get_value (DBusAddressEntry *entry,
+ const char *key)
+{
+ DBusList *values, *keys;
+
+ keys = _dbus_list_get_first_link (&entry->keys);
+ values = _dbus_list_get_first_link (&entry->values);
+
+ while (keys != NULL)
+ {
+ _dbus_assert (values != NULL);
+
+ if (_dbus_string_equal_c_str (keys->data, key))
+ return _dbus_string_get_const_data (values->data);
+
+ keys = _dbus_list_get_next_link (&entry->keys, keys);
+ values = _dbus_list_get_next_link (&entry->values, values);
+ }
+
+ return NULL;
+}
+
+static dbus_bool_t
+append_unescaped_value (DBusString *unescaped,
+ const DBusString *escaped,
+ int escaped_start,
+ int escaped_len,
+ DBusError *error)
+{
+ const char *p;
+ const char *end;
+ dbus_bool_t ret;
+
+ ret = FALSE;
+
+ p = _dbus_string_get_const_data (escaped) + escaped_start;
+ end = p + escaped_len;
+ while (p != end)
+ {
+ if (_DBUS_ADDRESS_OPTIONALLY_ESCAPED_BYTE (*p))
+ {
+ if (!_dbus_string_append_byte (unescaped, *p))
+ goto out;
+ }
+ else if (*p == '%')
+ {
+ /* Efficiency is king */
+ char buf[3];
+ DBusString hex;
+ int hex_end;
+
+ ++p;
+
+ if ((p + 2) > end)
+ {
+ dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS,
+ "In D-Bus address, percent character was not followed by two hex digits");
+ goto out;
+ }
+
+ buf[0] = *p;
+ ++p;
+ buf[1] = *p;
+ buf[2] = '\0';
+
+ _dbus_string_init_const (&hex, buf);
+
+ if (!_dbus_string_hex_decode (&hex, 0, &hex_end,
+ unescaped,
+ _dbus_string_get_length (unescaped)))
+ goto out;
+
+ if (hex_end != 2)
+ {
+ dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS,
+ "In D-Bus address, percent character was followed by characters other than hex digits");
+ goto out;
+ }
+ }
+ else
+ {
+ /* Error, should have been escaped */
+ dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS,
+ "In D-Bus address, character '%c' should have been escaped\n",
+ *p);
+ goto out;
+ }
+
+ ++p;
+ }
+
+ ret = TRUE;
+
+ out:
+ if (!ret && error && !dbus_error_is_set (error))
+ _DBUS_SET_OOM (error);
+
+ _dbus_assert (ret || error == NULL || dbus_error_is_set (error));
+
+ return ret;
+}
+
+/**
+ * Parses an address string of the form:
+ *
+ * method:key=value,key=value;method:key=value
+ *
+ * See the D-Bus specification for complete docs on the format.
+ *
+ * When connecting to an address, the first address entries
+ * in the semicolon-separated list should be tried first.
+ *
+ * @param address the address.
+ * @param entry_result return location to an array of entries.
+ * @param array_len return location for array length.
+ * @param error address where an error can be returned.
+ * @returns #TRUE on success, #FALSE otherwise.
+ */
+dbus_bool_t
+dbus_parse_address (const char *address,
+ DBusAddressEntry ***entry_result,
+ int *array_len,
+ DBusError *error)
+{
+ DBusString str;
+ int pos, end_pos, len, i;
+ DBusList *entries, *link;
+ DBusAddressEntry **entry_array;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ _dbus_string_init_const (&str, address);
+
+ entries = NULL;
+ pos = 0;
+ len = _dbus_string_get_length (&str);
+
+ if (len == 0)
+ {
+ dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS,
+ "Empty address '%s'", address);
+ goto error;
+ }
+
+ while (pos < len)
+ {
+ DBusAddressEntry *entry;
+
+ int found_pos;
+
+ entry = create_entry ();
+ if (!entry)
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+
+ goto error;
+ }
+
+ /* Append the entry */
+ if (!_dbus_list_append (&entries, entry))
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ dbus_address_entry_free (entry);
+ goto error;
+ }
+
+ /* Look for a semi-colon */
+ if (!_dbus_string_find (&str, pos, ";", &end_pos))
+ end_pos = len;
+
+ /* Look for the colon : */
+ if (!_dbus_string_find_to (&str, pos, end_pos, ":", &found_pos))
+ {
+ dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS, "Address does not contain a colon");
+ goto error;
+ }
+
+ if (!_dbus_string_copy_len (&str, pos, found_pos - pos, &entry->method, 0))
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ goto error;
+ }
+
+ pos = found_pos + 1;
+
+ while (pos < end_pos)
+ {
+ int comma_pos, equals_pos;
+
+ if (!_dbus_string_find_to (&str, pos, end_pos, ",", &comma_pos))
+ comma_pos = end_pos;
+
+ if (!_dbus_string_find_to (&str, pos, comma_pos, "=", &equals_pos) ||
+ equals_pos == pos || equals_pos + 1 == comma_pos)
+ {
+ dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS,
+ "'=' character not found or has no value following it");
+ goto error;
+ }
+ else
+ {
+ DBusString *key;
+ DBusString *value;
+
+ key = dbus_new0 (DBusString, 1);
+
+ if (!key)
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ goto error;
+ }
+
+ value = dbus_new0 (DBusString, 1);
+ if (!value)
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ dbus_free (key);
+ goto error;
+ }
+
+ if (!_dbus_string_init (key))
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ dbus_free (key);
+ dbus_free (value);
+
+ goto error;
+ }
+
+ if (!_dbus_string_init (value))
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ _dbus_string_free (key);
+
+ dbus_free (key);
+ dbus_free (value);
+ goto error;
+ }
+
+ if (!_dbus_string_copy_len (&str, pos, equals_pos - pos, key, 0))
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ _dbus_string_free (key);
+ _dbus_string_free (value);
+
+ dbus_free (key);
+ dbus_free (value);
+ goto error;
+ }
+
+ if (!append_unescaped_value (value, &str, equals_pos + 1,
+ comma_pos - equals_pos - 1, error))
+ {
+ _dbus_assert (error == NULL || dbus_error_is_set (error));
+ _dbus_string_free (key);
+ _dbus_string_free (value);
+
+ dbus_free (key);
+ dbus_free (value);
+ goto error;
+ }
+
+ if (!_dbus_list_append (&entry->keys, key))
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ _dbus_string_free (key);
+ _dbus_string_free (value);
+
+ dbus_free (key);
+ dbus_free (value);
+ goto error;
+ }
+
+ if (!_dbus_list_append (&entry->values, value))
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ _dbus_string_free (value);
+
+ dbus_free (value);
+ goto error;
+ }
+ }
+
+ pos = comma_pos + 1;
+ }
+
+ pos = end_pos + 1;
+ }
+
+ *array_len = _dbus_list_get_length (&entries);
+
+ entry_array = dbus_new (DBusAddressEntry *, *array_len + 1);
+
+ if (!entry_array)
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+
+ goto error;
+ }
+
+ entry_array [*array_len] = NULL;
+
+ link = _dbus_list_get_first_link (&entries);
+ i = 0;
+ while (link != NULL)
+ {
+ entry_array[i] = link->data;
+ i++;
+ link = _dbus_list_get_next_link (&entries, link);
+ }
+
+ _dbus_list_clear (&entries);
+ *entry_result = entry_array;
+
+ return TRUE;
+
+ error:
+
+ link = _dbus_list_get_first_link (&entries);
+ while (link != NULL)
+ {
+ dbus_address_entry_free (link->data);
+ link = _dbus_list_get_next_link (&entries, link);
+ }
+
+ _dbus_list_clear (&entries);
+
+ return FALSE;
+
+}
+
+/**
+ * Escapes the given string as a value in a key=value pair
+ * for a D-Bus address.
+ *
+ * @param value the unescaped value
+ * @returns newly-allocated escaped value or #NULL if no memory
+ */
+char*
+dbus_address_escape_value (const char *value)
+{
+ DBusString escaped;
+ DBusString unescaped;
+ char *ret;
+
+ ret = NULL;
+
+ _dbus_string_init_const (&unescaped, value);
+
+ if (!_dbus_string_init (&escaped))
+ return NULL;
+
+ if (!_dbus_address_append_escaped (&escaped, &unescaped))
+ goto out;
+
+ if (!_dbus_string_steal_data (&escaped, &ret))
+ goto out;
+
+ out:
+ _dbus_string_free (&escaped);
+ return ret;
+}
+
+/**
+ * Unescapes the given string as a value in a key=value pair
+ * for a D-Bus address. Note that dbus_address_entry_get_value()
+ * returns an already-unescaped value.
+ *
+ * @param value the escaped value
+ * @param error error to set if the unescaping fails
+ * @returns newly-allocated unescaped value or #NULL if no memory
+ */
+char*
+dbus_address_unescape_value (const char *value,
+ DBusError *error)
+{
+ DBusString unescaped;
+ DBusString escaped;
+ char *ret;
+
+ ret = NULL;
+
+ _dbus_string_init_const (&escaped, value);
+
+ if (!_dbus_string_init (&unescaped))
+ return NULL;
+
+ if (!append_unescaped_value (&unescaped, &escaped,
+ 0, _dbus_string_get_length (&escaped),
+ error))
+ goto out;
+
+ if (!_dbus_string_steal_data (&unescaped, &ret))
+ goto out;
+
+ out:
+ if (ret == NULL && error && !dbus_error_is_set (error))
+ _DBUS_SET_OOM (error);
+
+ _dbus_assert (ret != NULL || error == NULL || dbus_error_is_set (error));
+
+ _dbus_string_free (&unescaped);
+ return ret;
+}
+
+/** @} */ /* End of public API */
diff --git a/src/3rdparty/libdbus/dbus/dbus-address.h b/src/3rdparty/libdbus/dbus/dbus-address.h
new file mode 100644
index 00000000..c0303430
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-address.h
@@ -0,0 +1,87 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-address.h Server address parser.
+ *
+ * Copyright (C) 2003 CodeFactory AB
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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
+ *
+ */
+#if !defined (DBUS_INSIDE_DBUS_H) && !defined (DBUS_COMPILATION)
+#error "Only <dbus/dbus.h> can be included directly, this file may disappear or change contents."
+#endif
+
+#ifndef DBUS_ADDRESS_H
+#define DBUS_ADDRESS_H
+
+#include <dbus/dbus-types.h>
+#include <dbus/dbus-errors.h>
+
+DBUS_BEGIN_DECLS
+
+/**
+ * @addtogroup DBusAddress
+ * @{
+ */
+
+/** Opaque type representing one of the semicolon-separated items in an address */
+typedef struct DBusAddressEntry DBusAddressEntry;
+
+DBUS_EXPORT
+dbus_bool_t dbus_parse_address (const char *address,
+ DBusAddressEntry ***entry_result,
+ int *array_len,
+ DBusError *error);
+DBUS_EXPORT
+const char *dbus_address_entry_get_value (DBusAddressEntry *entry,
+ const char *key);
+DBUS_EXPORT
+const char *dbus_address_entry_get_method (DBusAddressEntry *entry);
+DBUS_EXPORT
+void dbus_address_entries_free (DBusAddressEntry **entries);
+
+DBUS_EXPORT
+char* dbus_address_escape_value (const char *value);
+DBUS_EXPORT
+char* dbus_address_unescape_value (const char *value,
+ DBusError *error);
+
+/**
+ * Clear a variable or struct member that contains an array of #DBusAddressEntry.
+ * If it does not contain #NULL, the entries that were previously
+ * there are freed with dbus_address_entries_free().
+ *
+ * This is similar to dbus_clear_connection(): see that function
+ * for more details.
+ *
+ * @param pointer_to_entries A pointer to a variable or struct member.
+ * pointer_to_entries must not be #NULL, but *pointer_to_entries
+ * may be #NULL.
+ */
+static inline void
+dbus_clear_address_entries (DBusAddressEntry ***pointer_to_entries)
+{
+ _dbus_clear_pointer_impl (DBusAddressEntry *, pointer_to_entries,
+ dbus_address_entries_free);
+}
+
+/** @} */
+
+DBUS_END_DECLS
+
+#endif /* DBUS_ADDRESS_H */
diff --git a/src/3rdparty/libdbus/dbus/dbus-arch-deps.h b/src/3rdparty/libdbus/dbus/dbus-arch-deps.h
new file mode 100644
index 00000000..29dea44f
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-arch-deps.h
@@ -0,0 +1,5 @@
+#if defined(PLATFORM_DBUS_ARCH_DEPS_H)
+# include PLATFORM_DBUS_ARCH_DEPS_H
+#else
+# error "This libdbus build is not available for this platform"
+#endif
diff --git a/src/3rdparty/libdbus/dbus/dbus-arch-deps.h.in b/src/3rdparty/libdbus/dbus/dbus-arch-deps.h.in
new file mode 100644
index 00000000..9dcb83cd
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-arch-deps.h.in
@@ -0,0 +1,65 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* dbus-arch-deps.h Header with architecture/compiler specific information, installed to libdir
+ *
+ * Copyright (C) 2003 Red Hat, Inc.
+ * SPDX-License-Identifier: AFL-2.0 OR GPL-2.0-or-later
+ *
+ * Licensed under the Academic Free License version 2.0
+ *
+ * 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
+ *
+ */
+#if !defined (DBUS_INSIDE_DBUS_H) && !defined (DBUS_COMPILATION)
+#error "Only <dbus/dbus.h> can be included directly, this file may disappear or change contents."
+#endif
+
+#ifndef DBUS_ARCH_DEPS_H
+#define DBUS_ARCH_DEPS_H
+
+#include <dbus/dbus-macros.h>
+
+DBUS_BEGIN_DECLS
+
+/* D-Bus no longer supports platforms with no 64-bit integer type. */
+#define DBUS_HAVE_INT64 1
+_DBUS_GNUC_EXTENSION typedef @DBUS_INT64_TYPE@ dbus_int64_t;
+_DBUS_GNUC_EXTENSION typedef unsigned @DBUS_INT64_TYPE@ dbus_uint64_t;
+#define DBUS_INT64_MODIFIER "@DBUS_INT64_MODIFIER@"
+
+#define DBUS_INT64_CONSTANT(val) (_DBUS_GNUC_EXTENSION @DBUS_INT64_CONSTANT@)
+#define DBUS_UINT64_CONSTANT(val) (_DBUS_GNUC_EXTENSION @DBUS_UINT64_CONSTANT@)
+
+typedef @DBUS_INT32_TYPE@ dbus_int32_t;
+typedef unsigned @DBUS_INT32_TYPE@ dbus_uint32_t;
+
+typedef @DBUS_INT16_TYPE@ dbus_int16_t;
+typedef unsigned @DBUS_INT16_TYPE@ dbus_uint16_t;
+
+#define DBUS_SIZEOF_VOID_P @DBUS_SIZEOF_VOID_P@
+
+/* This is not really arch-dependent, but it's not worth
+ * creating an additional generated header just for this
+ */
+#define DBUS_MAJOR_VERSION @DBUS_MAJOR_VERSION@
+#define DBUS_MINOR_VERSION @DBUS_MINOR_VERSION@
+#define DBUS_MICRO_VERSION @DBUS_MICRO_VERSION@
+
+#define DBUS_VERSION_STRING "@DBUS_VERSION@"
+
+#define DBUS_VERSION ((@DBUS_MAJOR_VERSION@ << 16) | (@DBUS_MINOR_VERSION@ << 8) | (@DBUS_MICRO_VERSION@))
+
+DBUS_END_DECLS
+
+#endif /* DBUS_ARCH_DEPS_H */
diff --git a/src/3rdparty/libdbus/dbus/dbus-auth.c b/src/3rdparty/libdbus/dbus/dbus-auth.c
new file mode 100644
index 00000000..09942f80
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-auth.c
@@ -0,0 +1,2970 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-auth.c Authentication
+ *
+ * Copyright (C) 2002, 2003, 2004 Red Hat Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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-auth.h"
+#include "dbus-string.h"
+#include "dbus-list.h"
+#include "dbus-internals.h"
+#include "dbus-keyring.h"
+#include "dbus-sha.h"
+#include "dbus-protocol.h"
+#include "dbus-credentials.h"
+
+/**
+ * @defgroup DBusAuth Authentication
+ * @ingroup DBusInternals
+ * @brief DBusAuth object
+ *
+ * DBusAuth manages the authentication negotiation when a connection
+ * is first established, and also manages any encryption used over a
+ * connection.
+ *
+ * @todo some SASL profiles require sending the empty string as a
+ * challenge/response, but we don't currently allow that in our
+ * protocol.
+ *
+ * @todo right now sometimes both ends will block waiting for input
+ * from the other end, e.g. if there's an error during
+ * DBUS_COOKIE_SHA1.
+ *
+ * @todo the cookie keyring needs to be cached globally not just
+ * per-auth (which raises threadsafety issues too)
+ *
+ * @todo grep FIXME in dbus-auth.c
+ */
+
+/**
+ * @defgroup DBusAuthInternals Authentication implementation details
+ * @ingroup DBusInternals
+ * @brief DBusAuth implementation details
+ *
+ * Private details of authentication code.
+ *
+ * @{
+ */
+
+/**
+ * This function appends an initial client response to the given string
+ */
+typedef dbus_bool_t (* DBusInitialResponseFunction) (DBusAuth *auth,
+ DBusString *response);
+
+/**
+ * This function processes a block of data received from the peer.
+ * i.e. handles a DATA command.
+ */
+typedef dbus_bool_t (* DBusAuthDataFunction) (DBusAuth *auth,
+ const DBusString *data);
+
+/**
+ * This function encodes a block of data from the peer.
+ */
+typedef dbus_bool_t (* DBusAuthEncodeFunction) (DBusAuth *auth,
+ const DBusString *data,
+ DBusString *encoded);
+
+/**
+ * This function decodes a block of data from the peer.
+ */
+typedef dbus_bool_t (* DBusAuthDecodeFunction) (DBusAuth *auth,
+ const DBusString *data,
+ DBusString *decoded);
+
+/**
+ * This function is called when the mechanism is abandoned.
+ */
+typedef void (* DBusAuthShutdownFunction) (DBusAuth *auth);
+
+/**
+ * Virtual table representing a particular auth mechanism.
+ */
+typedef struct
+{
+ const char *mechanism; /**< Name of the mechanism */
+ DBusAuthDataFunction server_data_func; /**< Function on server side for DATA */
+ DBusAuthEncodeFunction server_encode_func; /**< Function on server side to encode */
+ DBusAuthDecodeFunction server_decode_func; /**< Function on server side to decode */
+ DBusAuthShutdownFunction server_shutdown_func; /**< Function on server side to shut down */
+ DBusInitialResponseFunction client_initial_response_func; /**< Function on client side to handle initial response */
+ DBusAuthDataFunction client_data_func; /**< Function on client side for DATA */
+ DBusAuthEncodeFunction client_encode_func; /**< Function on client side for encode */
+ DBusAuthDecodeFunction client_decode_func; /**< Function on client side for decode */
+ DBusAuthShutdownFunction client_shutdown_func; /**< Function on client side for shutdown */
+} DBusAuthMechanismHandler;
+
+/**
+ * Enumeration for the known authentication commands.
+ */
+typedef enum {
+ DBUS_AUTH_COMMAND_AUTH,
+ DBUS_AUTH_COMMAND_CANCEL,
+ DBUS_AUTH_COMMAND_DATA,
+ DBUS_AUTH_COMMAND_BEGIN,
+ DBUS_AUTH_COMMAND_REJECTED,
+ DBUS_AUTH_COMMAND_OK,
+ DBUS_AUTH_COMMAND_ERROR,
+ DBUS_AUTH_COMMAND_UNKNOWN,
+ DBUS_AUTH_COMMAND_NEGOTIATE_UNIX_FD,
+ DBUS_AUTH_COMMAND_AGREE_UNIX_FD
+} DBusAuthCommand;
+
+/**
+ * Auth state function, determines the reaction to incoming events for
+ * a particular state. Returns whether we had enough memory to
+ * complete the operation.
+ */
+typedef dbus_bool_t (* DBusAuthStateFunction) (DBusAuth *auth,
+ DBusAuthCommand command,
+ const DBusString *args);
+
+/**
+ * Information about a auth state.
+ */
+typedef struct
+{
+ const char *name; /**< Name of the state */
+ DBusAuthStateFunction handler; /**< State function for this state */
+} DBusAuthStateData;
+
+/**
+ * Internal members of DBusAuth.
+ */
+struct DBusAuth
+{
+ int refcount; /**< reference count */
+ const char *side; /**< Client or server */
+
+ DBusString incoming; /**< Incoming data buffer */
+ DBusString outgoing; /**< Outgoing data buffer */
+
+ const DBusAuthStateData *state; /**< Current protocol state */
+
+ const DBusAuthMechanismHandler *mech; /**< Current auth mechanism */
+
+ DBusString identity; /**< Current identity we're authorizing
+ * as.
+ */
+
+ DBusCredentials *credentials; /**< Credentials read from socket
+ */
+
+ DBusCredentials *authorized_identity; /**< Credentials that are authorized */
+
+ DBusCredentials *desired_identity; /**< Identity client has requested */
+
+ DBusString context; /**< Cookie scope */
+ DBusKeyring *keyring; /**< Keyring for cookie mechanism. */
+ int cookie_id; /**< ID of cookie to use */
+ DBusString challenge; /**< Challenge sent to client */
+
+ char **allowed_mechs; /**< Mechanisms we're allowed to use,
+ * or #NULL if we can use any
+ */
+
+ unsigned int needed_memory : 1; /**< We needed memory to continue since last
+ * successful getting something done
+ */
+ unsigned int already_got_mechanisms : 1; /**< Client already got mech list */
+ unsigned int already_asked_for_initial_response : 1; /**< Already sent a blank challenge to get an initial response */
+ unsigned int buffer_outstanding : 1; /**< Buffer is "checked out" for reading data into */
+
+ unsigned int unix_fd_possible : 1; /**< This side could do unix fd passing */
+ unsigned int unix_fd_negotiated : 1; /**< Unix fd was successfully negotiated */
+};
+
+/**
+ * "Subclass" of DBusAuth for client side
+ */
+typedef struct
+{
+ DBusAuth base; /**< Parent class */
+
+ DBusList *mechs_to_try; /**< Mechanisms we got from the server that we're going to try using */
+
+ DBusString guid_from_server; /**< GUID received from server */
+
+} DBusAuthClient;
+
+/**
+ * "Subclass" of DBusAuth for server side.
+ */
+typedef struct
+{
+ DBusAuth base; /**< Parent class */
+
+ int failures; /**< Number of times client has been rejected */
+ int max_failures; /**< Number of times we reject before disconnect */
+
+ DBusString guid; /**< Our globally unique ID in hex encoding */
+
+} DBusAuthServer;
+
+static void goto_state (DBusAuth *auth,
+ const DBusAuthStateData *new_state);
+static dbus_bool_t send_auth (DBusAuth *auth,
+ const DBusAuthMechanismHandler *mech);
+static dbus_bool_t send_data (DBusAuth *auth,
+ DBusString *data);
+static dbus_bool_t send_rejected (DBusAuth *auth);
+static dbus_bool_t send_error (DBusAuth *auth,
+ const char *message);
+static dbus_bool_t send_ok (DBusAuth *auth);
+static dbus_bool_t send_begin (DBusAuth *auth);
+static dbus_bool_t send_cancel (DBusAuth *auth);
+static dbus_bool_t send_negotiate_unix_fd (DBusAuth *auth);
+static dbus_bool_t send_agree_unix_fd (DBusAuth *auth);
+
+/**
+ * Client states
+ */
+
+static dbus_bool_t handle_server_state_waiting_for_auth (DBusAuth *auth,
+ DBusAuthCommand command,
+ const DBusString *args);
+static dbus_bool_t handle_server_state_waiting_for_data (DBusAuth *auth,
+ DBusAuthCommand command,
+ const DBusString *args);
+static dbus_bool_t handle_server_state_waiting_for_begin (DBusAuth *auth,
+ DBusAuthCommand command,
+ const DBusString *args);
+
+static const DBusAuthStateData server_state_waiting_for_auth = {
+ "WaitingForAuth", handle_server_state_waiting_for_auth
+};
+static const DBusAuthStateData server_state_waiting_for_data = {
+ "WaitingForData", handle_server_state_waiting_for_data
+};
+static const DBusAuthStateData server_state_waiting_for_begin = {
+ "WaitingForBegin", handle_server_state_waiting_for_begin
+};
+
+/**
+ * Client states
+ */
+
+static dbus_bool_t handle_client_state_waiting_for_data (DBusAuth *auth,
+ DBusAuthCommand command,
+ const DBusString *args);
+static dbus_bool_t handle_client_state_waiting_for_ok (DBusAuth *auth,
+ DBusAuthCommand command,
+ const DBusString *args);
+static dbus_bool_t handle_client_state_waiting_for_reject (DBusAuth *auth,
+ DBusAuthCommand command,
+ const DBusString *args);
+static dbus_bool_t handle_client_state_waiting_for_agree_unix_fd (DBusAuth *auth,
+ DBusAuthCommand command,
+ const DBusString *args);
+
+static const DBusAuthStateData client_state_need_send_auth = {
+ "NeedSendAuth", NULL
+};
+static const DBusAuthStateData client_state_waiting_for_data = {
+ "WaitingForData", handle_client_state_waiting_for_data
+};
+/* The WaitingForOK state doesn't appear to be used.
+ * See https://bugs.freedesktop.org/show_bug.cgi?id=97298 */
+_DBUS_GNUC_UNUSED
+static const DBusAuthStateData client_state_waiting_for_ok = {
+ "WaitingForOK", handle_client_state_waiting_for_ok
+};
+static const DBusAuthStateData client_state_waiting_for_reject = {
+ "WaitingForReject", handle_client_state_waiting_for_reject
+};
+static const DBusAuthStateData client_state_waiting_for_agree_unix_fd = {
+ "WaitingForAgreeUnixFD", handle_client_state_waiting_for_agree_unix_fd
+};
+
+/**
+ * Common terminal states. Terminal states have handler == NULL.
+ */
+
+static const DBusAuthStateData common_state_authenticated = {
+ "Authenticated", NULL
+};
+
+static const DBusAuthStateData common_state_need_disconnect = {
+ "NeedDisconnect", NULL
+};
+
+static const char auth_side_client[] = "client";
+static const char auth_side_server[] = "server";
+/**
+ * @param auth the auth conversation
+ * @returns #TRUE if the conversation is the server side
+ */
+#define DBUS_AUTH_IS_SERVER(auth) ((auth)->side == auth_side_server)
+/**
+ * @param auth the auth conversation
+ * @returns #TRUE if the conversation is the client side
+ */
+#define DBUS_AUTH_IS_CLIENT(auth) ((auth)->side == auth_side_client)
+/**
+ * @param auth the auth conversation
+ * @returns auth cast to DBusAuthClient
+ */
+#define DBUS_AUTH_CLIENT(auth) ((DBusAuthClient*)(auth))
+/**
+ * @param auth the auth conversation
+ * @returns auth cast to DBusAuthServer
+ */
+#define DBUS_AUTH_SERVER(auth) ((DBusAuthServer*)(auth))
+
+/**
+ * The name of the auth ("client" or "server")
+ * @param auth the auth conversation
+ * @returns a string
+ */
+#define DBUS_AUTH_NAME(auth) ((auth)->side)
+
+static DBusAuth*
+_dbus_auth_new (int size)
+{
+ DBusAuth *auth;
+
+ auth = dbus_malloc0 (size);
+ if (auth == NULL)
+ return NULL;
+
+ auth->refcount = 1;
+
+ auth->keyring = NULL;
+ auth->cookie_id = -1;
+
+ /* note that we don't use the max string length feature,
+ * because you can't use that feature if you're going to
+ * try to recover from out-of-memory (it creates
+ * what looks like unrecoverable inability to alloc
+ * more space in the string). But we do handle
+ * overlong buffers in _dbus_auth_do_work().
+ */
+
+ if (!_dbus_string_init (&auth->incoming))
+ goto enomem_0;
+
+ if (!_dbus_string_init (&auth->outgoing))
+ goto enomem_1;
+
+ if (!_dbus_string_init (&auth->identity))
+ goto enomem_2;
+
+ if (!_dbus_string_init (&auth->context))
+ goto enomem_3;
+
+ if (!_dbus_string_init (&auth->challenge))
+ goto enomem_4;
+
+ /* default context if none is specified */
+ if (!_dbus_string_append (&auth->context, "org_freedesktop_general"))
+ goto enomem_5;
+
+ auth->credentials = _dbus_credentials_new ();
+ if (auth->credentials == NULL)
+ goto enomem_6;
+
+ auth->authorized_identity = _dbus_credentials_new ();
+ if (auth->authorized_identity == NULL)
+ goto enomem_7;
+
+ auth->desired_identity = _dbus_credentials_new ();
+ if (auth->desired_identity == NULL)
+ goto enomem_8;
+
+ return auth;
+
+#if 0
+ enomem_9:
+ _dbus_credentials_unref (auth->desired_identity);
+#endif
+ enomem_8:
+ _dbus_credentials_unref (auth->authorized_identity);
+ enomem_7:
+ _dbus_credentials_unref (auth->credentials);
+ enomem_6:
+ /* last alloc was an append to context, which is freed already below */ ;
+ enomem_5:
+ _dbus_string_free (&auth->challenge);
+ enomem_4:
+ _dbus_string_free (&auth->context);
+ enomem_3:
+ _dbus_string_free (&auth->identity);
+ enomem_2:
+ _dbus_string_free (&auth->outgoing);
+ enomem_1:
+ _dbus_string_free (&auth->incoming);
+ enomem_0:
+ dbus_free (auth);
+ return NULL;
+}
+
+static void
+shutdown_mech (DBusAuth *auth)
+{
+ /* Cancel any auth */
+ auth->already_asked_for_initial_response = FALSE;
+ _dbus_string_set_length (&auth->identity, 0);
+
+ _dbus_credentials_clear (auth->authorized_identity);
+ _dbus_credentials_clear (auth->desired_identity);
+
+ if (auth->mech != NULL)
+ {
+ _dbus_verbose ("%s: Shutting down mechanism %s\n",
+ DBUS_AUTH_NAME (auth), auth->mech->mechanism);
+
+ if (DBUS_AUTH_IS_CLIENT (auth))
+ (* auth->mech->client_shutdown_func) (auth);
+ else
+ (* auth->mech->server_shutdown_func) (auth);
+
+ auth->mech = NULL;
+ }
+}
+
+/*
+ * DBUS_COOKIE_SHA1 mechanism
+ */
+
+/* Returns TRUE but with an empty string hash if the
+ * cookie_id isn't known. As with all this code
+ * TRUE just means we had enough memory.
+ */
+static dbus_bool_t
+sha1_compute_hash (DBusAuth *auth,
+ int cookie_id,
+ const DBusString *server_challenge,
+ const DBusString *client_challenge,
+ DBusString *hash)
+{
+ DBusString cookie;
+ DBusString to_hash;
+ dbus_bool_t retval;
+
+ _dbus_assert (auth->keyring != NULL);
+
+ retval = FALSE;
+
+ if (!_dbus_string_init (&cookie))
+ return FALSE;
+
+ if (!_dbus_keyring_get_hex_key (auth->keyring, cookie_id,
+ &cookie))
+ goto out_0;
+
+ if (_dbus_string_get_length (&cookie) == 0)
+ {
+ retval = TRUE;
+ goto out_0;
+ }
+
+ if (!_dbus_string_init (&to_hash))
+ goto out_0;
+
+ if (!_dbus_string_copy (server_challenge, 0,
+ &to_hash, _dbus_string_get_length (&to_hash)))
+ goto out_1;
+
+ if (!_dbus_string_append (&to_hash, ":"))
+ goto out_1;
+
+ if (!_dbus_string_copy (client_challenge, 0,
+ &to_hash, _dbus_string_get_length (&to_hash)))
+ goto out_1;
+
+ if (!_dbus_string_append (&to_hash, ":"))
+ goto out_1;
+
+ if (!_dbus_string_copy (&cookie, 0,
+ &to_hash, _dbus_string_get_length (&to_hash)))
+ goto out_1;
+
+ if (!_dbus_sha_compute (&to_hash, hash))
+ goto out_1;
+
+ retval = TRUE;
+
+ out_1:
+ _dbus_string_zero (&to_hash);
+ _dbus_string_free (&to_hash);
+ out_0:
+ _dbus_string_zero (&cookie);
+ _dbus_string_free (&cookie);
+ return retval;
+}
+
+/** http://www.ietf.org/rfc/rfc2831.txt suggests at least 64 bits of
+ * entropy, we use 128. This is the number of bytes in the random
+ * challenge.
+ */
+#define N_CHALLENGE_BYTES (128/8)
+
+static dbus_bool_t
+sha1_handle_first_client_response (DBusAuth *auth,
+ const DBusString *data)
+{
+ /* We haven't sent a challenge yet, we're expecting a desired
+ * username from the client.
+ */
+ DBusString tmp = _DBUS_STRING_INIT_INVALID;
+ DBusString tmp2 = _DBUS_STRING_INIT_INVALID;
+ dbus_bool_t retval = FALSE;
+ DBusError error = DBUS_ERROR_INIT;
+ DBusCredentials *myself = NULL;
+
+ _dbus_string_set_length (&auth->challenge, 0);
+
+ if (_dbus_string_get_length (data) > 0)
+ {
+ if (_dbus_string_get_length (&auth->identity) > 0)
+ {
+ /* Tried to send two auth identities, wtf */
+ _dbus_verbose ("%s: client tried to send auth identity, but we already have one\n",
+ DBUS_AUTH_NAME (auth));
+ return send_rejected (auth);
+ }
+ else
+ {
+ /* this is our auth identity */
+ if (!_dbus_string_copy (data, 0, &auth->identity, 0))
+ return FALSE;
+ }
+ }
+
+ if (!_dbus_credentials_add_from_user (auth->desired_identity, data,
+ DBUS_CREDENTIALS_ADD_FLAGS_USER_DATABASE,
+ &error))
+ {
+ if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
+ {
+ dbus_error_free (&error);
+ return FALSE;
+ }
+
+ _dbus_verbose ("%s: Did not get a valid username from client: %s\n",
+ DBUS_AUTH_NAME (auth), error.message);
+ dbus_error_free (&error);
+ return send_rejected (auth);
+ }
+
+ if (!_dbus_string_init (&tmp))
+ return FALSE;
+
+ if (!_dbus_string_init (&tmp2))
+ {
+ _dbus_string_free (&tmp);
+ return FALSE;
+ }
+
+ myself = _dbus_credentials_new_from_current_process ();
+
+ if (myself == NULL)
+ goto out;
+
+ if (!_dbus_credentials_same_user (myself, auth->desired_identity))
+ {
+ /*
+ * DBUS_COOKIE_SHA1 is not suitable for authenticating that the
+ * client is anyone other than the user owning the process
+ * containing the DBusServer: we probably aren't allowed to write
+ * to other users' home directories. Even if we can (for example
+ * uid 0 on traditional Unix or CAP_DAC_OVERRIDE on Linux), we
+ * must not, because the other user controls their home directory,
+ * and could carry out symlink attacks to make us read from or
+ * write to unintended locations. It's difficult to avoid symlink
+ * attacks in a portable way, so we just don't try. This isn't a
+ * regression, because DBUS_COOKIE_SHA1 never worked for other
+ * users anyway.
+ */
+ _dbus_verbose ("%s: client tried to authenticate as \"%s\", "
+ "but that doesn't match this process",
+ DBUS_AUTH_NAME (auth),
+ _dbus_string_get_const_data (data));
+ retval = send_rejected (auth);
+ goto out;
+ }
+
+ /* we cache the keyring for speed, so here we drop it if it's the
+ * wrong one. FIXME caching the keyring here is useless since we use
+ * a different DBusAuth for every connection.
+ */
+ if (auth->keyring &&
+ !_dbus_keyring_is_for_credentials (auth->keyring,
+ auth->desired_identity))
+ {
+ _dbus_keyring_unref (auth->keyring);
+ auth->keyring = NULL;
+ }
+
+ if (auth->keyring == NULL)
+ {
+ auth->keyring = _dbus_keyring_new_for_credentials (auth->desired_identity,
+ &auth->context,
+ &error);
+
+ if (auth->keyring == NULL)
+ {
+ if (dbus_error_has_name (&error,
+ DBUS_ERROR_NO_MEMORY))
+ {
+ dbus_error_free (&error);
+ goto out;
+ }
+ else
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (&error);
+ _dbus_verbose ("%s: Error loading keyring: %s\n",
+ DBUS_AUTH_NAME (auth), error.message);
+ if (send_rejected (auth))
+ retval = TRUE; /* retval is only about mem */
+ dbus_error_free (&error);
+ goto out;
+ }
+ }
+ else
+ {
+ _dbus_assert (!dbus_error_is_set (&error));
+ }
+ }
+
+ _dbus_assert (auth->keyring != NULL);
+
+ auth->cookie_id = _dbus_keyring_get_best_key (auth->keyring, &error);
+ if (auth->cookie_id < 0)
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (&error);
+ _dbus_verbose ("%s: Could not get a cookie ID to send to client: %s\n",
+ DBUS_AUTH_NAME (auth), error.message);
+ if (send_rejected (auth))
+ retval = TRUE;
+ dbus_error_free (&error);
+ goto out;
+ }
+ else
+ {
+ _dbus_assert (!dbus_error_is_set (&error));
+ }
+
+ if (!_dbus_string_copy (&auth->context, 0,
+ &tmp2, _dbus_string_get_length (&tmp2)))
+ goto out;
+
+ if (!_dbus_string_append (&tmp2, " "))
+ goto out;
+
+ if (!_dbus_string_append_int (&tmp2, auth->cookie_id))
+ goto out;
+
+ if (!_dbus_string_append (&tmp2, " "))
+ goto out;
+
+ if (!_dbus_generate_random_bytes (&tmp, N_CHALLENGE_BYTES, &error))
+ {
+ if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
+ {
+ dbus_error_free (&error);
+ goto out;
+ }
+ else
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (&error);
+ _dbus_verbose ("%s: Error generating challenge: %s\n",
+ DBUS_AUTH_NAME (auth), error.message);
+ if (send_rejected (auth))
+ retval = TRUE; /* retval is only about mem */
+
+ dbus_error_free (&error);
+ goto out;
+ }
+ }
+
+ _dbus_string_set_length (&auth->challenge, 0);
+ if (!_dbus_string_hex_encode (&tmp, 0, &auth->challenge, 0))
+ goto out;
+
+ if (!_dbus_string_hex_encode (&tmp, 0, &tmp2,
+ _dbus_string_get_length (&tmp2)))
+ goto out;
+
+ if (!send_data (auth, &tmp2))
+ goto out;
+
+ goto_state (auth, &server_state_waiting_for_data);
+ retval = TRUE;
+
+ out:
+ _dbus_string_zero (&tmp);
+ _dbus_string_free (&tmp);
+ _dbus_string_zero (&tmp2);
+ _dbus_string_free (&tmp2);
+ _dbus_clear_credentials (&myself);
+
+ return retval;
+}
+
+static dbus_bool_t
+sha1_handle_second_client_response (DBusAuth *auth,
+ const DBusString *data)
+{
+ /* We are expecting a response which is the hex-encoded client
+ * challenge, space, then SHA-1 hash of the concatenation of our
+ * challenge, ":", client challenge, ":", secret key, all
+ * hex-encoded.
+ */
+ int i;
+ DBusString client_challenge;
+ DBusString client_hash;
+ dbus_bool_t retval;
+ DBusString correct_hash;
+
+ retval = FALSE;
+
+ if (!_dbus_string_find_blank (data, 0, &i))
+ {
+ _dbus_verbose ("%s: no space separator in client response\n",
+ DBUS_AUTH_NAME (auth));
+ return send_rejected (auth);
+ }
+
+ if (!_dbus_string_init (&client_challenge))
+ goto out_0;
+
+ if (!_dbus_string_init (&client_hash))
+ goto out_1;
+
+ if (!_dbus_string_copy_len (data, 0, i, &client_challenge,
+ 0))
+ goto out_2;
+
+ _dbus_string_skip_blank (data, i, &i);
+
+ if (!_dbus_string_copy_len (data, i,
+ _dbus_string_get_length (data) - i,
+ &client_hash,
+ 0))
+ goto out_2;
+
+ if (_dbus_string_get_length (&client_challenge) == 0 ||
+ _dbus_string_get_length (&client_hash) == 0)
+ {
+ _dbus_verbose ("%s: zero-length client challenge or hash\n",
+ DBUS_AUTH_NAME (auth));
+ if (send_rejected (auth))
+ retval = TRUE;
+ goto out_2;
+ }
+
+ if (!_dbus_string_init (&correct_hash))
+ goto out_2;
+
+ if (!sha1_compute_hash (auth, auth->cookie_id,
+ &auth->challenge,
+ &client_challenge,
+ &correct_hash))
+ goto out_3;
+
+ /* if cookie_id was invalid, then we get an empty hash */
+ if (_dbus_string_get_length (&correct_hash) == 0)
+ {
+ if (send_rejected (auth))
+ retval = TRUE;
+ goto out_3;
+ }
+
+ if (!_dbus_string_equal (&client_hash, &correct_hash))
+ {
+ if (send_rejected (auth))
+ retval = TRUE;
+ goto out_3;
+ }
+
+ if (!_dbus_credentials_add_credentials (auth->authorized_identity,
+ auth->desired_identity))
+ goto out_3;
+
+ /* Copy process ID (and PID FD) from the socket credentials if it's there
+ */
+ if (!_dbus_credentials_add_credential (auth->authorized_identity,
+ DBUS_CREDENTIAL_UNIX_PROCESS_FD,
+ auth->credentials))
+ goto out_3;
+ if (!_dbus_credentials_add_credential (auth->authorized_identity,
+ DBUS_CREDENTIAL_UNIX_PROCESS_ID,
+ auth->credentials))
+ goto out_3;
+
+ if (!send_ok (auth))
+ goto out_3;
+
+ _dbus_verbose ("%s: authenticated client using DBUS_COOKIE_SHA1\n",
+ DBUS_AUTH_NAME (auth));
+
+ retval = TRUE;
+
+ out_3:
+ _dbus_string_zero (&correct_hash);
+ _dbus_string_free (&correct_hash);
+ out_2:
+ _dbus_string_zero (&client_hash);
+ _dbus_string_free (&client_hash);
+ out_1:
+ _dbus_string_free (&client_challenge);
+ out_0:
+ return retval;
+}
+
+static dbus_bool_t
+handle_server_data_cookie_sha1_mech (DBusAuth *auth,
+ const DBusString *data)
+{
+ if (auth->cookie_id < 0)
+ return sha1_handle_first_client_response (auth, data);
+ else
+ return sha1_handle_second_client_response (auth, data);
+}
+
+static void
+handle_server_shutdown_cookie_sha1_mech (DBusAuth *auth)
+{
+ auth->cookie_id = -1;
+ _dbus_string_set_length (&auth->challenge, 0);
+}
+
+static dbus_bool_t
+handle_client_initial_response_cookie_sha1_mech (DBusAuth *auth,
+ DBusString *response)
+{
+ DBusString username;
+ dbus_bool_t retval;
+
+ retval = FALSE;
+
+ if (!_dbus_string_init (&username))
+ return FALSE;
+
+ if (!_dbus_append_user_from_current_process (&username))
+ goto out_0;
+
+ if (!_dbus_string_hex_encode (&username, 0,
+ response,
+ _dbus_string_get_length (response)))
+ goto out_0;
+
+ retval = TRUE;
+
+ out_0:
+ _dbus_string_free (&username);
+
+ return retval;
+}
+
+static dbus_bool_t
+handle_client_data_cookie_sha1_mech (DBusAuth *auth,
+ const DBusString *data)
+{
+ /* The data we get from the server should be the cookie context
+ * name, the cookie ID, and the server challenge, separated by
+ * spaces. We send back our challenge string and the correct hash.
+ */
+ dbus_bool_t retval = FALSE;
+ DBusString context;
+ DBusString cookie_id_str;
+ DBusString server_challenge;
+ DBusString client_challenge;
+ DBusString correct_hash;
+ DBusString tmp;
+ int i, j;
+ long val;
+ DBusError error = DBUS_ERROR_INIT;
+
+ if (!_dbus_string_find_blank (data, 0, &i))
+ {
+ if (send_error (auth,
+ "Server did not send context/ID/challenge properly"))
+ retval = TRUE;
+ goto out_0;
+ }
+
+ if (!_dbus_string_init (&context))
+ goto out_0;
+
+ if (!_dbus_string_copy_len (data, 0, i,
+ &context, 0))
+ goto out_1;
+
+ _dbus_string_skip_blank (data, i, &i);
+ if (!_dbus_string_find_blank (data, i, &j))
+ {
+ if (send_error (auth,
+ "Server did not send context/ID/challenge properly"))
+ retval = TRUE;
+ goto out_1;
+ }
+
+ if (!_dbus_string_init (&cookie_id_str))
+ goto out_1;
+
+ if (!_dbus_string_copy_len (data, i, j - i,
+ &cookie_id_str, 0))
+ goto out_2;
+
+ if (!_dbus_string_init (&server_challenge))
+ goto out_2;
+
+ i = j;
+ _dbus_string_skip_blank (data, i, &i);
+ j = _dbus_string_get_length (data);
+
+ if (!_dbus_string_copy_len (data, i, j - i,
+ &server_challenge, 0))
+ goto out_3;
+
+ if (!_dbus_keyring_validate_context (&context))
+ {
+ if (send_error (auth, "Server sent invalid cookie context"))
+ retval = TRUE;
+ goto out_3;
+ }
+
+ if (!_dbus_string_parse_int (&cookie_id_str, 0, &val, NULL))
+ {
+ if (send_error (auth, "Could not parse cookie ID as an integer"))
+ retval = TRUE;
+ goto out_3;
+ }
+
+ if (_dbus_string_get_length (&server_challenge) == 0)
+ {
+ if (send_error (auth, "Empty server challenge string"))
+ retval = TRUE;
+ goto out_3;
+ }
+
+ if (auth->keyring == NULL)
+ {
+ auth->keyring = _dbus_keyring_new_for_credentials (NULL,
+ &context,
+ &error);
+
+ if (auth->keyring == NULL)
+ {
+ if (dbus_error_has_name (&error,
+ DBUS_ERROR_NO_MEMORY))
+ {
+ dbus_error_free (&error);
+ goto out_3;
+ }
+ else
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (&error);
+
+ _dbus_verbose ("%s: Error loading keyring: %s\n",
+ DBUS_AUTH_NAME (auth), error.message);
+
+ if (send_error (auth, "Could not load cookie file"))
+ retval = TRUE; /* retval is only about mem */
+
+ dbus_error_free (&error);
+ goto out_3;
+ }
+ }
+ else
+ {
+ _dbus_assert (!dbus_error_is_set (&error));
+ }
+ }
+
+ _dbus_assert (auth->keyring != NULL);
+
+ if (!_dbus_string_init (&tmp))
+ goto out_3;
+
+ if (!_dbus_generate_random_bytes (&tmp, N_CHALLENGE_BYTES, &error))
+ {
+ if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
+ {
+ dbus_error_free (&error);
+ goto out_4;
+ }
+ else
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (&error);
+
+ _dbus_verbose ("%s: Failed to generate challenge: %s\n",
+ DBUS_AUTH_NAME (auth), error.message);
+
+ if (send_error (auth, "Failed to generate challenge"))
+ retval = TRUE; /* retval is only about mem */
+
+ dbus_error_free (&error);
+ goto out_4;
+ }
+ }
+
+ if (!_dbus_string_init (&client_challenge))
+ goto out_4;
+
+ if (!_dbus_string_hex_encode (&tmp, 0, &client_challenge, 0))
+ goto out_5;
+
+ if (!_dbus_string_init (&correct_hash))
+ goto out_5;
+
+ if (!sha1_compute_hash (auth, val,
+ &server_challenge,
+ &client_challenge,
+ &correct_hash))
+ goto out_6;
+
+ if (_dbus_string_get_length (&correct_hash) == 0)
+ {
+ /* couldn't find the cookie ID or something */
+ if (send_error (auth, "Don't have the requested cookie ID"))
+ retval = TRUE;
+ goto out_6;
+ }
+
+ _dbus_string_set_length (&tmp, 0);
+
+ if (!_dbus_string_copy (&client_challenge, 0, &tmp,
+ _dbus_string_get_length (&tmp)))
+ goto out_6;
+
+ if (!_dbus_string_append (&tmp, " "))
+ goto out_6;
+
+ if (!_dbus_string_copy (&correct_hash, 0, &tmp,
+ _dbus_string_get_length (&tmp)))
+ goto out_6;
+
+ if (!send_data (auth, &tmp))
+ goto out_6;
+
+ retval = TRUE;
+
+ out_6:
+ _dbus_string_zero (&correct_hash);
+ _dbus_string_free (&correct_hash);
+ out_5:
+ _dbus_string_free (&client_challenge);
+ out_4:
+ _dbus_string_zero (&tmp);
+ _dbus_string_free (&tmp);
+ out_3:
+ _dbus_string_free (&server_challenge);
+ out_2:
+ _dbus_string_free (&cookie_id_str);
+ out_1:
+ _dbus_string_free (&context);
+ out_0:
+ return retval;
+}
+
+static void
+handle_client_shutdown_cookie_sha1_mech (DBusAuth *auth)
+{
+ auth->cookie_id = -1;
+ _dbus_string_set_length (&auth->challenge, 0);
+}
+
+/*
+ * EXTERNAL mechanism
+ */
+
+static dbus_bool_t
+handle_server_data_external_mech (DBusAuth *auth,
+ const DBusString *data)
+{
+ if (_dbus_credentials_are_anonymous (auth->credentials))
+ {
+ _dbus_verbose ("%s: no credentials, mechanism EXTERNAL can't authenticate\n",
+ DBUS_AUTH_NAME (auth));
+ return send_rejected (auth);
+ }
+
+ if (_dbus_string_get_length (data) > 0)
+ {
+ if (_dbus_string_get_length (&auth->identity) > 0)
+ {
+ /* Tried to send two auth identities, wtf */
+ _dbus_verbose ("%s: client tried to send auth identity, but we already have one\n",
+ DBUS_AUTH_NAME (auth));
+ return send_rejected (auth);
+ }
+ else
+ {
+ /* this is our auth identity */
+ if (!_dbus_string_copy (data, 0, &auth->identity, 0))
+ return FALSE;
+ }
+ }
+
+ /* Poke client for an auth identity, if none given */
+ if (_dbus_string_get_length (&auth->identity) == 0 &&
+ !auth->already_asked_for_initial_response)
+ {
+ if (send_data (auth, NULL))
+ {
+ _dbus_verbose ("%s: sending empty challenge asking client for auth identity\n",
+ DBUS_AUTH_NAME (auth));
+ auth->already_asked_for_initial_response = TRUE;
+ goto_state (auth, &server_state_waiting_for_data);
+ return TRUE;
+ }
+ else
+ return FALSE;
+ }
+
+ _dbus_credentials_clear (auth->desired_identity);
+
+ /* If auth->identity is still empty here, then client
+ * responded with an empty string after we poked it for
+ * an initial response. This means to try to auth the
+ * identity provided in the credentials.
+ */
+ if (_dbus_string_get_length (&auth->identity) == 0)
+ {
+ if (!_dbus_credentials_add_credentials (auth->desired_identity,
+ auth->credentials))
+ {
+ return FALSE; /* OOM */
+ }
+ }
+ else
+ {
+ DBusError error = DBUS_ERROR_INIT;
+
+ if (!_dbus_credentials_add_from_user (auth->desired_identity,
+ &auth->identity,
+ DBUS_CREDENTIALS_ADD_FLAGS_NONE,
+ &error))
+ {
+ if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
+ {
+ dbus_error_free (&error);
+ return FALSE;
+ }
+
+ _dbus_verbose ("%s: could not get credentials from uid string: %s\n",
+ DBUS_AUTH_NAME (auth), error.message);
+ dbus_error_free (&error);
+ return send_rejected (auth);
+ }
+ }
+
+ if (_dbus_credentials_are_anonymous (auth->desired_identity))
+ {
+ _dbus_verbose ("%s: desired user %s is no good\n",
+ DBUS_AUTH_NAME (auth),
+ _dbus_string_get_const_data (&auth->identity));
+ return send_rejected (auth);
+ }
+
+ if (_dbus_credentials_are_superset (auth->credentials,
+ auth->desired_identity))
+ {
+ /* client has authenticated */
+ if (!_dbus_credentials_add_credentials (auth->authorized_identity,
+ auth->desired_identity))
+ return FALSE;
+
+ /* also copy misc process info from the socket credentials
+ */
+ if (!_dbus_credentials_add_credential (auth->authorized_identity,
+ DBUS_CREDENTIAL_UNIX_PROCESS_FD,
+ auth->credentials))
+ return FALSE;
+
+ if (!_dbus_credentials_add_credential (auth->authorized_identity,
+ DBUS_CREDENTIAL_UNIX_PROCESS_ID,
+ auth->credentials))
+ return FALSE;
+
+ if (!_dbus_credentials_add_credential (auth->authorized_identity,
+ DBUS_CREDENTIAL_ADT_AUDIT_DATA_ID,
+ auth->credentials))
+ return FALSE;
+
+ if (!_dbus_credentials_add_credential (auth->authorized_identity,
+ DBUS_CREDENTIAL_UNIX_GROUP_IDS,
+ auth->credentials))
+ return FALSE;
+
+ if (!_dbus_credentials_add_credential (auth->authorized_identity,
+ DBUS_CREDENTIAL_LINUX_SECURITY_LABEL,
+ auth->credentials))
+ return FALSE;
+
+ if (!send_ok (auth))
+ return FALSE;
+
+ _dbus_verbose ("%s: authenticated client based on socket credentials\n",
+ DBUS_AUTH_NAME (auth));
+
+ return TRUE;
+ }
+ else
+ {
+ _dbus_verbose ("%s: desired identity not found in socket credentials\n",
+ DBUS_AUTH_NAME (auth));
+ return send_rejected (auth);
+ }
+}
+
+static void
+handle_server_shutdown_external_mech (DBusAuth *auth)
+{
+
+}
+
+static dbus_bool_t
+handle_client_initial_response_external_mech (DBusAuth *auth,
+ DBusString *response)
+{
+ /* We always append our UID as an initial response, so the server
+ * doesn't have to send back an empty challenge to check whether we
+ * want to specify an identity. i.e. this avoids a round trip that
+ * the spec for the EXTERNAL mechanism otherwise requires.
+ */
+ DBusString plaintext;
+
+ if (!_dbus_string_init (&plaintext))
+ return FALSE;
+
+ if (!_dbus_append_user_from_current_process (&plaintext))
+ goto failed;
+
+ if (!_dbus_string_hex_encode (&plaintext, 0,
+ response,
+ _dbus_string_get_length (response)))
+ goto failed;
+
+ _dbus_string_free (&plaintext);
+
+ return TRUE;
+
+ failed:
+ _dbus_string_free (&plaintext);
+ return FALSE;
+}
+
+static dbus_bool_t
+handle_client_data_external_mech (DBusAuth *auth,
+ const DBusString *data)
+{
+
+ return TRUE;
+}
+
+static void
+handle_client_shutdown_external_mech (DBusAuth *auth)
+{
+
+}
+
+/*
+ * ANONYMOUS mechanism
+ */
+
+static dbus_bool_t
+handle_server_data_anonymous_mech (DBusAuth *auth,
+ const DBusString *data)
+{
+ if (_dbus_string_get_length (data) > 0)
+ {
+ /* Client is allowed to send "trace" data, the only defined
+ * meaning is that if it contains '@' it is an email address,
+ * and otherwise it is anything else, and it's supposed to be
+ * UTF-8
+ */
+ if (!_dbus_string_validate_utf8 (data, 0, _dbus_string_get_length (data)))
+ {
+ _dbus_verbose ("%s: Received invalid UTF-8 trace data from ANONYMOUS client\n",
+ DBUS_AUTH_NAME (auth));
+ return send_rejected (auth);
+ }
+
+ _dbus_verbose ("%s: ANONYMOUS client sent trace string: '%s'\n",
+ DBUS_AUTH_NAME (auth),
+ _dbus_string_get_const_data (data));
+ }
+
+ /* We want to be anonymous (clear in case some other protocol got midway through I guess) */
+ _dbus_credentials_clear (auth->desired_identity);
+
+ /* Copy process ID (and PID FD) from the socket credentials
+ */
+ if (!_dbus_credentials_add_credential (auth->authorized_identity,
+ DBUS_CREDENTIAL_UNIX_PROCESS_FD,
+ auth->credentials))
+ return FALSE;
+
+ if (!_dbus_credentials_add_credential (auth->authorized_identity,
+ DBUS_CREDENTIAL_UNIX_PROCESS_ID,
+ auth->credentials))
+ return FALSE;
+
+ /* Anonymous is always allowed */
+ if (!send_ok (auth))
+ return FALSE;
+
+ _dbus_verbose ("%s: authenticated client as anonymous\n",
+ DBUS_AUTH_NAME (auth));
+
+ return TRUE;
+}
+
+static void
+handle_server_shutdown_anonymous_mech (DBusAuth *auth)
+{
+
+}
+
+static dbus_bool_t
+handle_client_initial_response_anonymous_mech (DBusAuth *auth,
+ DBusString *response)
+{
+ /* Our initial response is a "trace" string which must be valid UTF-8
+ * and must be an email address if it contains '@'.
+ * We just send the dbus implementation info, like a user-agent or
+ * something, because... why not. There's nothing guaranteed here
+ * though, we could change it later.
+ */
+ DBusString plaintext;
+
+ if (!_dbus_string_init (&plaintext))
+ return FALSE;
+
+ if (!_dbus_string_append (&plaintext,
+ "libdbus " DBUS_VERSION_STRING))
+ goto failed;
+
+ if (!_dbus_string_hex_encode (&plaintext, 0,
+ response,
+ _dbus_string_get_length (response)))
+ goto failed;
+
+ _dbus_string_free (&plaintext);
+
+ return TRUE;
+
+ failed:
+ _dbus_string_free (&plaintext);
+ return FALSE;
+}
+
+static dbus_bool_t
+handle_client_data_anonymous_mech (DBusAuth *auth,
+ const DBusString *data)
+{
+
+ return TRUE;
+}
+
+static void
+handle_client_shutdown_anonymous_mech (DBusAuth *auth)
+{
+
+}
+
+/* Put mechanisms here in order of preference.
+ * Right now we have:
+ *
+ * - EXTERNAL checks socket credentials (or in the future, other info from the OS)
+ * - DBUS_COOKIE_SHA1 uses a cookie in the home directory, like xauth or ICE
+ * - ANONYMOUS checks nothing but doesn't auth the person as a user
+ *
+ * We might ideally add a mechanism to chain to Cyrus SASL so we can
+ * use its mechanisms as well.
+ *
+ */
+static const DBusAuthMechanismHandler
+all_mechanisms[] = {
+ { "EXTERNAL",
+ handle_server_data_external_mech,
+ NULL, NULL,
+ handle_server_shutdown_external_mech,
+ handle_client_initial_response_external_mech,
+ handle_client_data_external_mech,
+ NULL, NULL,
+ handle_client_shutdown_external_mech },
+ { "DBUS_COOKIE_SHA1",
+ handle_server_data_cookie_sha1_mech,
+ NULL, NULL,
+ handle_server_shutdown_cookie_sha1_mech,
+ handle_client_initial_response_cookie_sha1_mech,
+ handle_client_data_cookie_sha1_mech,
+ NULL, NULL,
+ handle_client_shutdown_cookie_sha1_mech },
+ { "ANONYMOUS",
+ handle_server_data_anonymous_mech,
+ NULL, NULL,
+ handle_server_shutdown_anonymous_mech,
+ handle_client_initial_response_anonymous_mech,
+ handle_client_data_anonymous_mech,
+ NULL, NULL,
+ handle_client_shutdown_anonymous_mech },
+ { NULL, NULL }
+};
+
+static const DBusAuthMechanismHandler*
+find_mech (const DBusString *name,
+ char **allowed_mechs)
+{
+ int i;
+
+ if (allowed_mechs != NULL &&
+ !_dbus_string_array_contains ((const char**) allowed_mechs,
+ _dbus_string_get_const_data (name)))
+ return NULL;
+
+ i = 0;
+ while (all_mechanisms[i].mechanism != NULL)
+ {
+ if (_dbus_string_equal_c_str (name,
+ all_mechanisms[i].mechanism))
+
+ return &all_mechanisms[i];
+
+ ++i;
+ }
+
+ return NULL;
+}
+
+static dbus_bool_t
+send_auth (DBusAuth *auth, const DBusAuthMechanismHandler *mech)
+{
+ DBusString auth_command;
+
+ if (!_dbus_string_init (&auth_command))
+ return FALSE;
+
+ if (!_dbus_string_append (&auth_command,
+ "AUTH "))
+ {
+ _dbus_string_free (&auth_command);
+ return FALSE;
+ }
+
+ if (!_dbus_string_append (&auth_command,
+ mech->mechanism))
+ {
+ _dbus_string_free (&auth_command);
+ return FALSE;
+ }
+
+ if (mech->client_initial_response_func != NULL)
+ {
+ if (!_dbus_string_append (&auth_command, " "))
+ {
+ _dbus_string_free (&auth_command);
+ return FALSE;
+ }
+
+ if (!(* mech->client_initial_response_func) (auth, &auth_command))
+ {
+ _dbus_string_free (&auth_command);
+ return FALSE;
+ }
+ }
+
+ if (!_dbus_string_append (&auth_command,
+ "\r\n"))
+ {
+ _dbus_string_free (&auth_command);
+ return FALSE;
+ }
+
+ if (!_dbus_string_copy (&auth_command, 0,
+ &auth->outgoing,
+ _dbus_string_get_length (&auth->outgoing)))
+ {
+ _dbus_string_free (&auth_command);
+ return FALSE;
+ }
+
+ _dbus_string_free (&auth_command);
+ shutdown_mech (auth);
+ auth->mech = mech;
+ goto_state (auth, &client_state_waiting_for_data);
+
+ return TRUE;
+}
+
+static dbus_bool_t
+send_data (DBusAuth *auth, DBusString *data)
+{
+ int old_len;
+
+ if (data == NULL || _dbus_string_get_length (data) == 0)
+ return _dbus_string_append (&auth->outgoing, "DATA\r\n");
+ else
+ {
+ old_len = _dbus_string_get_length (&auth->outgoing);
+ if (!_dbus_string_append (&auth->outgoing, "DATA "))
+ goto out;
+
+ if (!_dbus_string_hex_encode (data, 0, &auth->outgoing,
+ _dbus_string_get_length (&auth->outgoing)))
+ goto out;
+
+ if (!_dbus_string_append (&auth->outgoing, "\r\n"))
+ goto out;
+
+ return TRUE;
+
+ out:
+ _dbus_string_set_length (&auth->outgoing, old_len);
+
+ return FALSE;
+ }
+}
+
+static dbus_bool_t
+send_rejected (DBusAuth *auth)
+{
+ DBusString command;
+ DBusAuthServer *server_auth;
+ int i;
+
+ if (!_dbus_string_init (&command))
+ return FALSE;
+
+ if (!_dbus_string_append (&command,
+ "REJECTED"))
+ goto nomem;
+
+ for (i = 0; all_mechanisms[i].mechanism != NULL; i++)
+ {
+ /* skip mechanisms that aren't allowed */
+ if (auth->allowed_mechs != NULL &&
+ !_dbus_string_array_contains ((const char**)auth->allowed_mechs,
+ all_mechanisms[i].mechanism))
+ continue;
+
+ if (!_dbus_string_append (&command,
+ " "))
+ goto nomem;
+
+ if (!_dbus_string_append (&command,
+ all_mechanisms[i].mechanism))
+ goto nomem;
+ }
+
+ if (!_dbus_string_append (&command, "\r\n"))
+ goto nomem;
+
+ if (!_dbus_string_copy (&command, 0, &auth->outgoing,
+ _dbus_string_get_length (&auth->outgoing)))
+ goto nomem;
+
+ shutdown_mech (auth);
+
+ _dbus_assert (DBUS_AUTH_IS_SERVER (auth));
+ server_auth = DBUS_AUTH_SERVER (auth);
+ server_auth->failures += 1;
+
+ if (server_auth->failures >= server_auth->max_failures)
+ goto_state (auth, &common_state_need_disconnect);
+ else
+ goto_state (auth, &server_state_waiting_for_auth);
+
+ _dbus_string_free (&command);
+
+ return TRUE;
+
+ nomem:
+ _dbus_string_free (&command);
+ return FALSE;
+}
+
+static dbus_bool_t
+send_error (DBusAuth *auth, const char *message)
+{
+ return _dbus_string_append_printf (&auth->outgoing,
+ "ERROR \"%s\"\r\n", message);
+}
+
+static dbus_bool_t
+send_ok (DBusAuth *auth)
+{
+ int orig_len;
+
+ orig_len = _dbus_string_get_length (&auth->outgoing);
+
+ if (_dbus_string_append (&auth->outgoing, "OK ") &&
+ _dbus_string_copy (& DBUS_AUTH_SERVER (auth)->guid,
+ 0,
+ &auth->outgoing,
+ _dbus_string_get_length (&auth->outgoing)) &&
+ _dbus_string_append (&auth->outgoing, "\r\n"))
+ {
+ goto_state (auth, &server_state_waiting_for_begin);
+ return TRUE;
+ }
+ else
+ {
+ _dbus_string_set_length (&auth->outgoing, orig_len);
+ return FALSE;
+ }
+}
+
+static dbus_bool_t
+send_begin (DBusAuth *auth)
+{
+
+ if (!_dbus_string_append (&auth->outgoing,
+ "BEGIN\r\n"))
+ return FALSE;
+
+ goto_state (auth, &common_state_authenticated);
+ return TRUE;
+}
+
+static dbus_bool_t
+process_ok(DBusAuth *auth,
+ const DBusString *args_from_ok) {
+
+ int end_of_hex;
+
+ /* "args_from_ok" should be the GUID, whitespace already pulled off the front */
+ _dbus_assert (_dbus_string_get_length (& DBUS_AUTH_CLIENT (auth)->guid_from_server) == 0);
+
+ /* We decode the hex string to binary, using guid_from_server as scratch... */
+
+ end_of_hex = 0;
+ if (!_dbus_string_hex_decode (args_from_ok, 0, &end_of_hex,
+ & DBUS_AUTH_CLIENT (auth)->guid_from_server, 0))
+ return FALSE;
+
+ /* now clear out the scratch */
+ _dbus_string_set_length (& DBUS_AUTH_CLIENT (auth)->guid_from_server, 0);
+
+ if (end_of_hex != _dbus_string_get_length (args_from_ok) ||
+ end_of_hex == 0)
+ {
+ _dbus_verbose ("Bad GUID from server, parsed %d bytes and had %d bytes from server\n",
+ end_of_hex, _dbus_string_get_length (args_from_ok));
+ goto_state (auth, &common_state_need_disconnect);
+ return TRUE;
+ }
+
+ if (!_dbus_string_copy (args_from_ok, 0, &DBUS_AUTH_CLIENT (auth)->guid_from_server, 0)) {
+ _dbus_string_set_length (& DBUS_AUTH_CLIENT (auth)->guid_from_server, 0);
+ return FALSE;
+ }
+
+ _dbus_verbose ("Got GUID '%s' from the server\n",
+ _dbus_string_get_const_data (& DBUS_AUTH_CLIENT (auth)->guid_from_server));
+
+ if (auth->unix_fd_possible)
+ {
+ if (!send_negotiate_unix_fd (auth))
+ {
+ _dbus_string_set_length (& DBUS_AUTH_CLIENT (auth)->guid_from_server, 0);
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+
+ _dbus_verbose("Not negotiating unix fd passing, since not possible\n");
+
+ if (!send_begin (auth))
+ {
+ _dbus_string_set_length (& DBUS_AUTH_CLIENT (auth)->guid_from_server, 0);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static dbus_bool_t
+send_cancel (DBusAuth *auth)
+{
+ if (_dbus_string_append (&auth->outgoing, "CANCEL\r\n"))
+ {
+ goto_state (auth, &client_state_waiting_for_reject);
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+static dbus_bool_t
+process_data (DBusAuth *auth,
+ const DBusString *args,
+ DBusAuthDataFunction data_func)
+{
+ int end;
+ DBusString decoded;
+
+ if (!_dbus_string_init (&decoded))
+ return FALSE;
+
+ if (!_dbus_string_hex_decode (args, 0, &end, &decoded, 0))
+ {
+ _dbus_string_free (&decoded);
+ return FALSE;
+ }
+
+ if (_dbus_string_get_length (args) != end)
+ {
+ _dbus_string_free (&decoded);
+ if (!send_error (auth, "Invalid hex encoding"))
+ return FALSE;
+
+ return TRUE;
+ }
+
+#ifdef DBUS_ENABLE_VERBOSE_MODE
+ if (_dbus_string_validate_ascii (&decoded, 0,
+ _dbus_string_get_length (&decoded)))
+ _dbus_verbose ("%s: data: '%s'\n",
+ DBUS_AUTH_NAME (auth),
+ _dbus_string_get_const_data (&decoded));
+#endif
+
+ if (!(* data_func) (auth, &decoded))
+ {
+ _dbus_string_free (&decoded);
+ return FALSE;
+ }
+
+ _dbus_string_free (&decoded);
+ return TRUE;
+}
+
+static dbus_bool_t
+send_negotiate_unix_fd (DBusAuth *auth)
+{
+ if (!_dbus_string_append (&auth->outgoing,
+ "NEGOTIATE_UNIX_FD\r\n"))
+ return FALSE;
+
+ goto_state (auth, &client_state_waiting_for_agree_unix_fd);
+ return TRUE;
+}
+
+static dbus_bool_t
+send_agree_unix_fd (DBusAuth *auth)
+{
+ _dbus_assert(auth->unix_fd_possible);
+
+ auth->unix_fd_negotiated = TRUE;
+ _dbus_verbose("Agreed to UNIX FD passing\n");
+
+ if (!_dbus_string_append (&auth->outgoing,
+ "AGREE_UNIX_FD\r\n"))
+ return FALSE;
+
+ goto_state (auth, &server_state_waiting_for_begin);
+ return TRUE;
+}
+
+static dbus_bool_t
+handle_auth (DBusAuth *auth, const DBusString *args)
+{
+ if (_dbus_string_get_length (args) == 0)
+ {
+ /* No args to the auth, send mechanisms */
+ if (!send_rejected (auth))
+ return FALSE;
+
+ return TRUE;
+ }
+ else
+ {
+ int i;
+ DBusString mech;
+ DBusString hex_response;
+
+ _dbus_string_find_blank (args, 0, &i);
+
+ if (!_dbus_string_init (&mech))
+ return FALSE;
+
+ if (!_dbus_string_init (&hex_response))
+ {
+ _dbus_string_free (&mech);
+ return FALSE;
+ }
+
+ if (!_dbus_string_copy_len (args, 0, i, &mech, 0))
+ goto failed;
+
+ _dbus_string_skip_blank (args, i, &i);
+ if (!_dbus_string_copy (args, i, &hex_response, 0))
+ goto failed;
+
+ auth->mech = find_mech (&mech, auth->allowed_mechs);
+ if (auth->mech != NULL)
+ {
+ _dbus_verbose ("%s: Trying mechanism %s\n",
+ DBUS_AUTH_NAME (auth),
+ auth->mech->mechanism);
+
+ if (!process_data (auth, &hex_response,
+ auth->mech->server_data_func))
+ goto failed;
+ }
+ else
+ {
+ /* Unsupported mechanism */
+ _dbus_verbose ("%s: Unsupported mechanism %s\n",
+ DBUS_AUTH_NAME (auth),
+ _dbus_string_get_const_data (&mech));
+
+ if (!send_rejected (auth))
+ goto failed;
+ }
+
+ _dbus_string_free (&mech);
+ _dbus_string_free (&hex_response);
+
+ return TRUE;
+
+ failed:
+ auth->mech = NULL;
+ _dbus_string_free (&mech);
+ _dbus_string_free (&hex_response);
+ return FALSE;
+ }
+}
+
+static dbus_bool_t
+handle_server_state_waiting_for_auth (DBusAuth *auth,
+ DBusAuthCommand command,
+ const DBusString *args)
+{
+ switch (command)
+ {
+ case DBUS_AUTH_COMMAND_AUTH:
+ return handle_auth (auth, args);
+
+ case DBUS_AUTH_COMMAND_CANCEL:
+ case DBUS_AUTH_COMMAND_DATA:
+ return send_error (auth, "Not currently in an auth conversation");
+
+ case DBUS_AUTH_COMMAND_BEGIN:
+ goto_state (auth, &common_state_need_disconnect);
+ return TRUE;
+
+ case DBUS_AUTH_COMMAND_ERROR:
+ return send_rejected (auth);
+
+ case DBUS_AUTH_COMMAND_NEGOTIATE_UNIX_FD:
+ return send_error (auth, "Need to authenticate first");
+
+ case DBUS_AUTH_COMMAND_REJECTED:
+ case DBUS_AUTH_COMMAND_OK:
+ case DBUS_AUTH_COMMAND_UNKNOWN:
+ case DBUS_AUTH_COMMAND_AGREE_UNIX_FD:
+ default:
+ return send_error (auth, "Unknown command");
+ }
+}
+
+static dbus_bool_t
+handle_server_state_waiting_for_data (DBusAuth *auth,
+ DBusAuthCommand command,
+ const DBusString *args)
+{
+ switch (command)
+ {
+ case DBUS_AUTH_COMMAND_AUTH:
+ return send_error (auth, "Sent AUTH while another AUTH in progress");
+
+ case DBUS_AUTH_COMMAND_CANCEL:
+ case DBUS_AUTH_COMMAND_ERROR:
+ return send_rejected (auth);
+
+ case DBUS_AUTH_COMMAND_DATA:
+ return process_data (auth, args, auth->mech->server_data_func);
+
+ case DBUS_AUTH_COMMAND_BEGIN:
+ goto_state (auth, &common_state_need_disconnect);
+ return TRUE;
+
+ case DBUS_AUTH_COMMAND_NEGOTIATE_UNIX_FD:
+ return send_error (auth, "Need to authenticate first");
+
+ case DBUS_AUTH_COMMAND_REJECTED:
+ case DBUS_AUTH_COMMAND_OK:
+ case DBUS_AUTH_COMMAND_UNKNOWN:
+ case DBUS_AUTH_COMMAND_AGREE_UNIX_FD:
+ default:
+ return send_error (auth, "Unknown command");
+ }
+}
+
+static dbus_bool_t
+handle_server_state_waiting_for_begin (DBusAuth *auth,
+ DBusAuthCommand command,
+ const DBusString *args)
+{
+ switch (command)
+ {
+ case DBUS_AUTH_COMMAND_AUTH:
+ return send_error (auth, "Sent AUTH while expecting BEGIN");
+
+ case DBUS_AUTH_COMMAND_DATA:
+ return send_error (auth, "Sent DATA while expecting BEGIN");
+
+ case DBUS_AUTH_COMMAND_BEGIN:
+ goto_state (auth, &common_state_authenticated);
+ return TRUE;
+
+ case DBUS_AUTH_COMMAND_NEGOTIATE_UNIX_FD:
+ if (auth->unix_fd_possible)
+ return send_agree_unix_fd(auth);
+ else
+ return send_error(auth, "Unix FD passing not supported, not authenticated or otherwise not possible");
+
+ case DBUS_AUTH_COMMAND_REJECTED:
+ case DBUS_AUTH_COMMAND_OK:
+ case DBUS_AUTH_COMMAND_UNKNOWN:
+ case DBUS_AUTH_COMMAND_AGREE_UNIX_FD:
+ default:
+ return send_error (auth, "Unknown command");
+
+ case DBUS_AUTH_COMMAND_CANCEL:
+ case DBUS_AUTH_COMMAND_ERROR:
+ return send_rejected (auth);
+ }
+}
+
+/* return FALSE if no memory, TRUE if all OK */
+static dbus_bool_t
+get_word (const DBusString *str,
+ int *start,
+ DBusString *word)
+{
+ int i;
+
+ _dbus_string_skip_blank (str, *start, start);
+ _dbus_string_find_blank (str, *start, &i);
+
+ if (i > *start)
+ {
+ if (!_dbus_string_copy_len (str, *start, i - *start, word, 0))
+ return FALSE;
+
+ *start = i;
+ }
+
+ return TRUE;
+}
+
+static dbus_bool_t
+record_mechanisms (DBusAuth *auth,
+ const DBusString *args)
+{
+ int next;
+ int len;
+
+ if (auth->already_got_mechanisms)
+ return TRUE;
+
+ len = _dbus_string_get_length (args);
+
+ next = 0;
+ while (next < len)
+ {
+ DBusString m;
+ const DBusAuthMechanismHandler *mech;
+
+ if (!_dbus_string_init (&m))
+ goto nomem;
+
+ if (!get_word (args, &next, &m))
+ {
+ _dbus_string_free (&m);
+ goto nomem;
+ }
+
+ mech = find_mech (&m, auth->allowed_mechs);
+
+ if (mech != NULL)
+ {
+ /* FIXME right now we try mechanisms in the order
+ * the server lists them; should we do them in
+ * some more deterministic order?
+ *
+ * Probably in all_mechanisms order, our order of
+ * preference. Of course when the server is us,
+ * it lists things in that order anyhow.
+ */
+
+ if (mech != &all_mechanisms[0])
+ {
+ _dbus_verbose ("%s: Adding mechanism %s to list we will try\n",
+ DBUS_AUTH_NAME (auth), mech->mechanism);
+
+ if (!_dbus_list_append (& DBUS_AUTH_CLIENT (auth)->mechs_to_try,
+ (void*) mech))
+ {
+ _dbus_string_free (&m);
+ goto nomem;
+ }
+ }
+ else
+ {
+ _dbus_verbose ("%s: Already tried mechanism %s; not adding to list we will try\n",
+ DBUS_AUTH_NAME (auth), mech->mechanism);
+ }
+ }
+ else
+ {
+ _dbus_verbose ("%s: Server offered mechanism \"%s\" that we don't know how to use\n",
+ DBUS_AUTH_NAME (auth),
+ _dbus_string_get_const_data (&m));
+ }
+
+ _dbus_string_free (&m);
+ }
+
+ auth->already_got_mechanisms = TRUE;
+
+ return TRUE;
+
+ nomem:
+ _dbus_list_clear (& DBUS_AUTH_CLIENT (auth)->mechs_to_try);
+
+ return FALSE;
+}
+
+static dbus_bool_t
+process_rejected (DBusAuth *auth, const DBusString *args)
+{
+ const DBusAuthMechanismHandler *mech;
+ DBusAuthClient *client;
+
+ client = DBUS_AUTH_CLIENT (auth);
+
+ if (!auth->already_got_mechanisms)
+ {
+ if (!record_mechanisms (auth, args))
+ return FALSE;
+ }
+
+ if (DBUS_AUTH_CLIENT (auth)->mechs_to_try != NULL)
+ {
+ mech = client->mechs_to_try->data;
+
+ if (!send_auth (auth, mech))
+ return FALSE;
+
+ _dbus_list_pop_first (&client->mechs_to_try);
+
+ _dbus_verbose ("%s: Trying mechanism %s\n",
+ DBUS_AUTH_NAME (auth),
+ mech->mechanism);
+ }
+ else
+ {
+ /* Give up */
+ _dbus_verbose ("%s: Disconnecting because we are out of mechanisms to try using\n",
+ DBUS_AUTH_NAME (auth));
+ goto_state (auth, &common_state_need_disconnect);
+ }
+
+ return TRUE;
+}
+
+
+static dbus_bool_t
+handle_client_state_waiting_for_data (DBusAuth *auth,
+ DBusAuthCommand command,
+ const DBusString *args)
+{
+ _dbus_assert (auth->mech != NULL);
+
+ switch (command)
+ {
+ case DBUS_AUTH_COMMAND_DATA:
+ return process_data (auth, args, auth->mech->client_data_func);
+
+ case DBUS_AUTH_COMMAND_REJECTED:
+ return process_rejected (auth, args);
+
+ case DBUS_AUTH_COMMAND_OK:
+ return process_ok(auth, args);
+
+ case DBUS_AUTH_COMMAND_ERROR:
+ return send_cancel (auth);
+
+ case DBUS_AUTH_COMMAND_AUTH:
+ case DBUS_AUTH_COMMAND_CANCEL:
+ case DBUS_AUTH_COMMAND_BEGIN:
+ case DBUS_AUTH_COMMAND_UNKNOWN:
+ case DBUS_AUTH_COMMAND_NEGOTIATE_UNIX_FD:
+ case DBUS_AUTH_COMMAND_AGREE_UNIX_FD:
+ default:
+ return send_error (auth, "Unknown command");
+ }
+}
+
+static dbus_bool_t
+handle_client_state_waiting_for_ok (DBusAuth *auth,
+ DBusAuthCommand command,
+ const DBusString *args)
+{
+ switch (command)
+ {
+ case DBUS_AUTH_COMMAND_REJECTED:
+ return process_rejected (auth, args);
+
+ case DBUS_AUTH_COMMAND_OK:
+ return process_ok(auth, args);
+
+ case DBUS_AUTH_COMMAND_DATA:
+ case DBUS_AUTH_COMMAND_ERROR:
+ return send_cancel (auth);
+
+ case DBUS_AUTH_COMMAND_AUTH:
+ case DBUS_AUTH_COMMAND_CANCEL:
+ case DBUS_AUTH_COMMAND_BEGIN:
+ case DBUS_AUTH_COMMAND_UNKNOWN:
+ case DBUS_AUTH_COMMAND_NEGOTIATE_UNIX_FD:
+ case DBUS_AUTH_COMMAND_AGREE_UNIX_FD:
+ default:
+ return send_error (auth, "Unknown command");
+ }
+}
+
+static dbus_bool_t
+handle_client_state_waiting_for_reject (DBusAuth *auth,
+ DBusAuthCommand command,
+ const DBusString *args)
+{
+ switch (command)
+ {
+ case DBUS_AUTH_COMMAND_REJECTED:
+ return process_rejected (auth, args);
+
+ case DBUS_AUTH_COMMAND_AUTH:
+ case DBUS_AUTH_COMMAND_CANCEL:
+ case DBUS_AUTH_COMMAND_DATA:
+ case DBUS_AUTH_COMMAND_BEGIN:
+ case DBUS_AUTH_COMMAND_OK:
+ case DBUS_AUTH_COMMAND_ERROR:
+ case DBUS_AUTH_COMMAND_UNKNOWN:
+ case DBUS_AUTH_COMMAND_NEGOTIATE_UNIX_FD:
+ case DBUS_AUTH_COMMAND_AGREE_UNIX_FD:
+ default:
+ goto_state (auth, &common_state_need_disconnect);
+ return TRUE;
+ }
+}
+
+static dbus_bool_t
+handle_client_state_waiting_for_agree_unix_fd(DBusAuth *auth,
+ DBusAuthCommand command,
+ const DBusString *args)
+{
+ switch (command)
+ {
+ case DBUS_AUTH_COMMAND_AGREE_UNIX_FD:
+ _dbus_assert(auth->unix_fd_possible);
+ auth->unix_fd_negotiated = TRUE;
+ _dbus_verbose("Successfully negotiated UNIX FD passing\n");
+ return send_begin (auth);
+
+ case DBUS_AUTH_COMMAND_ERROR:
+ _dbus_assert(auth->unix_fd_possible);
+ auth->unix_fd_negotiated = FALSE;
+ _dbus_verbose("Failed to negotiate UNIX FD passing\n");
+ return send_begin (auth);
+
+ case DBUS_AUTH_COMMAND_OK:
+ case DBUS_AUTH_COMMAND_DATA:
+ case DBUS_AUTH_COMMAND_REJECTED:
+ case DBUS_AUTH_COMMAND_AUTH:
+ case DBUS_AUTH_COMMAND_CANCEL:
+ case DBUS_AUTH_COMMAND_BEGIN:
+ case DBUS_AUTH_COMMAND_UNKNOWN:
+ case DBUS_AUTH_COMMAND_NEGOTIATE_UNIX_FD:
+ default:
+ return send_error (auth, "Unknown command");
+ }
+}
+
+/**
+ * Mapping from command name to enum
+ */
+typedef struct {
+ const char *name; /**< Name of the command */
+ DBusAuthCommand command; /**< Corresponding enum */
+} DBusAuthCommandName;
+
+static const DBusAuthCommandName auth_command_names[] = {
+ { "AUTH", DBUS_AUTH_COMMAND_AUTH },
+ { "CANCEL", DBUS_AUTH_COMMAND_CANCEL },
+ { "DATA", DBUS_AUTH_COMMAND_DATA },
+ { "BEGIN", DBUS_AUTH_COMMAND_BEGIN },
+ { "REJECTED", DBUS_AUTH_COMMAND_REJECTED },
+ { "OK", DBUS_AUTH_COMMAND_OK },
+ { "ERROR", DBUS_AUTH_COMMAND_ERROR },
+ { "NEGOTIATE_UNIX_FD", DBUS_AUTH_COMMAND_NEGOTIATE_UNIX_FD },
+ { "AGREE_UNIX_FD", DBUS_AUTH_COMMAND_AGREE_UNIX_FD }
+};
+
+static DBusAuthCommand
+lookup_command_from_name (DBusString *command)
+{
+ int i;
+
+ for (i = 0; i < _DBUS_N_ELEMENTS (auth_command_names); i++)
+ {
+ if (_dbus_string_equal_c_str (command,
+ auth_command_names[i].name))
+ return auth_command_names[i].command;
+ }
+
+ return DBUS_AUTH_COMMAND_UNKNOWN;
+}
+
+static void
+goto_state (DBusAuth *auth,
+ const DBusAuthStateData *state)
+{
+ _dbus_verbose ("%s: going from state %s to state %s\n",
+ DBUS_AUTH_NAME (auth),
+ auth->state->name,
+ state->name);
+
+ auth->state = state;
+}
+
+/* returns whether to call it again right away */
+static dbus_bool_t
+process_command (DBusAuth *auth)
+{
+ DBusAuthCommand command;
+ DBusString line;
+ DBusString args;
+ int eol;
+ int i, j;
+ dbus_bool_t retval;
+
+ /* _dbus_verbose ("%s: trying process_command()\n"); */
+
+ retval = FALSE;
+
+ eol = 0;
+ if (!_dbus_string_find (&auth->incoming, 0, "\r\n", &eol))
+ return FALSE;
+
+ if (!_dbus_string_init (&line))
+ {
+ auth->needed_memory = TRUE;
+ return FALSE;
+ }
+
+ if (!_dbus_string_init (&args))
+ {
+ _dbus_string_free (&line);
+ auth->needed_memory = TRUE;
+ return FALSE;
+ }
+
+ if (!_dbus_string_copy_len (&auth->incoming, 0, eol, &line, 0))
+ goto out;
+
+ if (!_dbus_string_validate_ascii (&line, 0,
+ _dbus_string_get_length (&line)))
+ {
+ _dbus_verbose ("%s: Command contained non-ASCII chars or embedded nul\n",
+ DBUS_AUTH_NAME (auth));
+ if (!send_error (auth, "Command contained non-ASCII"))
+ goto out;
+ else
+ goto next_command;
+ }
+
+ _dbus_verbose ("%s: got command \"%s\"\n",
+ DBUS_AUTH_NAME (auth),
+ _dbus_string_get_const_data (&line));
+
+ _dbus_string_find_blank (&line, 0, &i);
+ _dbus_string_skip_blank (&line, i, &j);
+
+ if (j > i)
+ _dbus_string_delete (&line, i, j - i);
+
+ if (!_dbus_string_move (&line, i, &args, 0))
+ goto out;
+
+ /* FIXME 1.0 we should probably validate that only the allowed
+ * chars are in the command name
+ */
+
+ command = lookup_command_from_name (&line);
+ if (!(* auth->state->handler) (auth, command, &args))
+ goto out;
+
+ next_command:
+
+ /* We've succeeded in processing the whole command so drop it out
+ * of the incoming buffer and return TRUE to try another command.
+ */
+
+ _dbus_string_delete (&auth->incoming, 0, eol);
+
+ /* kill the \r\n */
+ _dbus_string_delete (&auth->incoming, 0, 2);
+
+ retval = TRUE;
+
+ out:
+ _dbus_string_free (&args);
+ _dbus_string_free (&line);
+
+ if (!retval)
+ auth->needed_memory = TRUE;
+ else
+ auth->needed_memory = FALSE;
+
+ return retval;
+}
+
+
+/** @} */
+
+/**
+ * @addtogroup DBusAuth
+ * @{
+ */
+
+/**
+ * Creates a new auth conversation object for the server side.
+ * See http://dbus.freedesktop.org/doc/dbus-specification.html#auth-protocol
+ * for full details on what this object does.
+ *
+ * @returns the new object or #NULL if no memory
+ */
+DBusAuth*
+_dbus_auth_server_new (const DBusString *guid)
+{
+ DBusAuth *auth;
+ DBusAuthServer *server_auth;
+ DBusString guid_copy;
+
+ if (!_dbus_string_init (&guid_copy))
+ return NULL;
+
+ if (!_dbus_string_copy (guid, 0, &guid_copy, 0))
+ {
+ _dbus_string_free (&guid_copy);
+ return NULL;
+ }
+
+ auth = _dbus_auth_new (sizeof (DBusAuthServer));
+ if (auth == NULL)
+ {
+ _dbus_string_free (&guid_copy);
+ return NULL;
+ }
+
+ auth->side = auth_side_server;
+ auth->state = &server_state_waiting_for_auth;
+
+ server_auth = DBUS_AUTH_SERVER (auth);
+
+ server_auth->guid = guid_copy;
+
+ /* perhaps this should be per-mechanism with a lower
+ * max
+ */
+ server_auth->failures = 0;
+ server_auth->max_failures = 6;
+
+ return auth;
+}
+
+/**
+ * Creates a new auth conversation object for the client side.
+ * See http://dbus.freedesktop.org/doc/dbus-specification.html#auth-protocol
+ * for full details on what this object does.
+ *
+ * @returns the new object or #NULL if no memory
+ */
+DBusAuth*
+_dbus_auth_client_new (void)
+{
+ DBusAuth *auth;
+ DBusString guid_str;
+
+ if (!_dbus_string_init (&guid_str))
+ return NULL;
+
+ auth = _dbus_auth_new (sizeof (DBusAuthClient));
+ if (auth == NULL)
+ {
+ _dbus_string_free (&guid_str);
+ return NULL;
+ }
+
+ DBUS_AUTH_CLIENT (auth)->guid_from_server = guid_str;
+
+ auth->side = auth_side_client;
+ auth->state = &client_state_need_send_auth;
+
+ /* Start the auth conversation by sending AUTH for our default
+ * mechanism */
+ if (!send_auth (auth, &all_mechanisms[0]))
+ {
+ _dbus_auth_unref (auth);
+ return NULL;
+ }
+
+ return auth;
+}
+
+/**
+ * Increments the refcount of an auth object.
+ *
+ * @param auth the auth conversation
+ * @returns the auth conversation
+ */
+DBusAuth *
+_dbus_auth_ref (DBusAuth *auth)
+{
+ _dbus_assert (auth != NULL);
+
+ auth->refcount += 1;
+
+ return auth;
+}
+
+/**
+ * Decrements the refcount of an auth object.
+ *
+ * @param auth the auth conversation
+ */
+void
+_dbus_auth_unref (DBusAuth *auth)
+{
+ _dbus_assert (auth != NULL);
+ _dbus_assert (auth->refcount > 0);
+
+ auth->refcount -= 1;
+ if (auth->refcount == 0)
+ {
+ shutdown_mech (auth);
+
+ if (DBUS_AUTH_IS_CLIENT (auth))
+ {
+ _dbus_string_free (& DBUS_AUTH_CLIENT (auth)->guid_from_server);
+ _dbus_list_clear (& DBUS_AUTH_CLIENT (auth)->mechs_to_try);
+ }
+ else
+ {
+ _dbus_assert (DBUS_AUTH_IS_SERVER (auth));
+
+ _dbus_string_free (& DBUS_AUTH_SERVER (auth)->guid);
+ }
+
+ if (auth->keyring)
+ _dbus_keyring_unref (auth->keyring);
+
+ _dbus_string_free (&auth->context);
+ _dbus_string_free (&auth->challenge);
+ _dbus_string_free (&auth->identity);
+ _dbus_string_free (&auth->incoming);
+ _dbus_string_free (&auth->outgoing);
+
+ dbus_free_string_array (auth->allowed_mechs);
+
+ _dbus_credentials_unref (auth->credentials);
+ _dbus_credentials_unref (auth->authorized_identity);
+ _dbus_credentials_unref (auth->desired_identity);
+
+ dbus_free (auth);
+ }
+}
+
+/**
+ * Sets an array of authentication mechanism names
+ * that we are willing to use.
+ *
+ * @param auth the auth conversation
+ * @param mechanisms #NULL-terminated array of mechanism names
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_auth_set_mechanisms (DBusAuth *auth,
+ const char **mechanisms)
+{
+ char **copy;
+
+ if (mechanisms != NULL)
+ {
+ copy = _dbus_dup_string_array (mechanisms);
+ if (copy == NULL)
+ return FALSE;
+ }
+ else
+ copy = NULL;
+
+ dbus_free_string_array (auth->allowed_mechs);
+
+ auth->allowed_mechs = copy;
+
+ return TRUE;
+}
+
+/**
+ * @param auth the auth conversation object
+ * @returns #TRUE if we're in a final state
+ */
+#define DBUS_AUTH_IN_END_STATE(auth) ((auth)->state->handler == NULL)
+
+/**
+ * Analyzes buffered input and moves the auth conversation forward,
+ * returning the new state of the auth conversation.
+ *
+ * @param auth the auth conversation
+ * @returns the new state
+ */
+DBusAuthState
+_dbus_auth_do_work (DBusAuth *auth)
+{
+ auth->needed_memory = FALSE;
+
+ /* Max amount we'll buffer up before deciding someone's on crack */
+#define MAX_BUFFER (16 * _DBUS_ONE_KILOBYTE)
+
+ do
+ {
+ if (DBUS_AUTH_IN_END_STATE (auth))
+ break;
+
+ if (_dbus_string_get_length (&auth->incoming) > MAX_BUFFER ||
+ _dbus_string_get_length (&auth->outgoing) > MAX_BUFFER)
+ {
+ goto_state (auth, &common_state_need_disconnect);
+ _dbus_verbose ("%s: Disconnecting due to excessive data buffered in auth phase\n",
+ DBUS_AUTH_NAME (auth));
+ break;
+ }
+ }
+ while (process_command (auth));
+
+ if (auth->needed_memory)
+ return DBUS_AUTH_STATE_WAITING_FOR_MEMORY;
+ else if (_dbus_string_get_length (&auth->outgoing) > 0)
+ return DBUS_AUTH_STATE_HAVE_BYTES_TO_SEND;
+ else if (auth->state == &common_state_need_disconnect)
+ return DBUS_AUTH_STATE_NEED_DISCONNECT;
+ else if (auth->state == &common_state_authenticated)
+ return DBUS_AUTH_STATE_AUTHENTICATED;
+ else return DBUS_AUTH_STATE_WAITING_FOR_INPUT;
+}
+
+/**
+ * Gets bytes that need to be sent to the peer we're conversing with.
+ * After writing some bytes, _dbus_auth_bytes_sent() must be called
+ * to notify the auth object that they were written.
+ *
+ * @param auth the auth conversation
+ * @param str return location for a ref to the buffer to send
+ * @returns #FALSE if nothing to send
+ */
+dbus_bool_t
+_dbus_auth_get_bytes_to_send (DBusAuth *auth,
+ const DBusString **str)
+{
+ _dbus_assert (auth != NULL);
+ _dbus_assert (str != NULL);
+
+ *str = NULL;
+
+ if (_dbus_string_get_length (&auth->outgoing) == 0)
+ return FALSE;
+
+ *str = &auth->outgoing;
+
+ return TRUE;
+}
+
+/**
+ * Notifies the auth conversation object that
+ * the given number of bytes of the outgoing buffer
+ * have been written out.
+ *
+ * @param auth the auth conversation
+ * @param bytes_sent number of bytes written out
+ */
+void
+_dbus_auth_bytes_sent (DBusAuth *auth,
+ int bytes_sent)
+{
+ _dbus_verbose ("%s: Sent %d bytes of: %s\n",
+ DBUS_AUTH_NAME (auth),
+ bytes_sent,
+ _dbus_string_get_const_data (&auth->outgoing));
+
+ _dbus_string_delete (&auth->outgoing,
+ 0, bytes_sent);
+}
+
+/**
+ * Get a buffer to be used for reading bytes from the peer we're conversing
+ * with. Bytes should be appended to this buffer.
+ *
+ * @param auth the auth conversation
+ * @param buffer return location for buffer to append bytes to
+ */
+void
+_dbus_auth_get_buffer (DBusAuth *auth,
+ DBusString **buffer)
+{
+ _dbus_assert (auth != NULL);
+ _dbus_assert (!auth->buffer_outstanding);
+
+ *buffer = &auth->incoming;
+
+ auth->buffer_outstanding = TRUE;
+}
+
+/**
+ * Returns a buffer with new data read into it.
+ *
+ * @param auth the auth conversation
+ * @param buffer the buffer being returned
+ */
+void
+_dbus_auth_return_buffer (DBusAuth *auth,
+ DBusString *buffer)
+{
+ _dbus_assert (buffer == &auth->incoming);
+ _dbus_assert (auth->buffer_outstanding);
+
+ auth->buffer_outstanding = FALSE;
+}
+
+/**
+ * Returns leftover bytes that were not used as part of the auth
+ * conversation. These bytes will be part of the message stream
+ * instead. This function may not be called until authentication has
+ * succeeded.
+ *
+ * @param auth the auth conversation
+ * @param str return location for pointer to string of unused bytes
+ */
+void
+_dbus_auth_get_unused_bytes (DBusAuth *auth,
+ const DBusString **str)
+{
+ if (!DBUS_AUTH_IN_END_STATE (auth))
+ return;
+
+ *str = &auth->incoming;
+}
+
+
+/**
+ * Gets rid of unused bytes returned by _dbus_auth_get_unused_bytes()
+ * after we've gotten them and successfully moved them elsewhere.
+ *
+ * @param auth the auth conversation
+ */
+void
+_dbus_auth_delete_unused_bytes (DBusAuth *auth)
+{
+ if (!DBUS_AUTH_IN_END_STATE (auth))
+ return;
+
+ _dbus_string_set_length (&auth->incoming, 0);
+}
+
+/**
+ * Called post-authentication, indicates whether we need to encode
+ * the message stream with _dbus_auth_encode_data() prior to
+ * sending it to the peer.
+ *
+ * @param auth the auth conversation
+ * @returns #TRUE if we need to encode the stream
+ */
+dbus_bool_t
+_dbus_auth_needs_encoding (DBusAuth *auth)
+{
+ if (auth->state != &common_state_authenticated)
+ return FALSE;
+
+ if (auth->mech != NULL)
+ {
+ if (DBUS_AUTH_IS_CLIENT (auth))
+ return auth->mech->client_encode_func != NULL;
+ else
+ return auth->mech->server_encode_func != NULL;
+ }
+ else
+ return FALSE;
+}
+
+/**
+ * Called post-authentication, encodes a block of bytes for sending to
+ * the peer. If no encoding was negotiated, just copies the bytes
+ * (you can avoid this by checking _dbus_auth_needs_encoding()).
+ *
+ * @param auth the auth conversation
+ * @param plaintext the plain text data
+ * @param encoded initialized string to where encoded data is appended
+ * @returns #TRUE if we had enough memory and successfully encoded
+ */
+dbus_bool_t
+_dbus_auth_encode_data (DBusAuth *auth,
+ const DBusString *plaintext,
+ DBusString *encoded)
+{
+ _dbus_assert (plaintext != encoded);
+
+ if (auth->state != &common_state_authenticated)
+ return FALSE;
+
+ if (_dbus_auth_needs_encoding (auth))
+ {
+ if (DBUS_AUTH_IS_CLIENT (auth))
+ return (* auth->mech->client_encode_func) (auth, plaintext, encoded);
+ else
+ return (* auth->mech->server_encode_func) (auth, plaintext, encoded);
+ }
+ else
+ {
+ return _dbus_string_copy (plaintext, 0, encoded,
+ _dbus_string_get_length (encoded));
+ }
+}
+
+/**
+ * Called post-authentication, indicates whether we need to decode
+ * the message stream with _dbus_auth_decode_data() after
+ * receiving it from the peer.
+ *
+ * @param auth the auth conversation
+ * @returns #TRUE if we need to encode the stream
+ */
+dbus_bool_t
+_dbus_auth_needs_decoding (DBusAuth *auth)
+{
+ if (auth->state != &common_state_authenticated)
+ return FALSE;
+
+ if (auth->mech != NULL)
+ {
+ if (DBUS_AUTH_IS_CLIENT (auth))
+ return auth->mech->client_decode_func != NULL;
+ else
+ return auth->mech->server_decode_func != NULL;
+ }
+ else
+ return FALSE;
+}
+
+
+/**
+ * Called post-authentication, decodes a block of bytes received from
+ * the peer. If no encoding was negotiated, just copies the bytes (you
+ * can avoid this by checking _dbus_auth_needs_decoding()).
+ *
+ * @todo 1.0? We need to be able to distinguish "out of memory" error
+ * from "the data is hosed" error.
+ *
+ * @param auth the auth conversation
+ * @param encoded the encoded data
+ * @param plaintext initialized string where decoded data is appended
+ * @returns #TRUE if we had enough memory and successfully decoded
+ */
+dbus_bool_t
+_dbus_auth_decode_data (DBusAuth *auth,
+ const DBusString *encoded,
+ DBusString *plaintext)
+{
+ _dbus_assert (plaintext != encoded);
+
+ if (auth->state != &common_state_authenticated)
+ return FALSE;
+
+ if (_dbus_auth_needs_decoding (auth))
+ {
+ if (DBUS_AUTH_IS_CLIENT (auth))
+ return (* auth->mech->client_decode_func) (auth, encoded, plaintext);
+ else
+ return (* auth->mech->server_decode_func) (auth, encoded, plaintext);
+ }
+ else
+ {
+ return _dbus_string_copy (encoded, 0, plaintext,
+ _dbus_string_get_length (plaintext));
+ }
+}
+
+/**
+ * Sets credentials received via reliable means from the operating
+ * system.
+ *
+ * @param auth the auth conversation
+ * @param credentials the credentials received
+ * @returns #FALSE on OOM
+ */
+dbus_bool_t
+_dbus_auth_set_credentials (DBusAuth *auth,
+ DBusCredentials *credentials)
+{
+ _dbus_credentials_clear (auth->credentials);
+ return _dbus_credentials_add_credentials (auth->credentials,
+ credentials);
+}
+
+/**
+ * Gets the identity we authorized the client as. Apps may have
+ * different policies as to what identities they allow.
+ *
+ * Returned credentials are not a copy and should not be modified
+ *
+ * @param auth the auth conversation
+ * @returns the credentials we've authorized BY REFERENCE do not modify
+ */
+DBusCredentials*
+_dbus_auth_get_identity (DBusAuth *auth)
+{
+ if (auth->state == &common_state_authenticated)
+ {
+ return auth->authorized_identity;
+ }
+ else
+ {
+ /* FIXME instead of this, keep an empty credential around that
+ * doesn't require allocation or something
+ */
+ /* return empty credentials */
+ _dbus_assert (_dbus_credentials_are_empty (auth->authorized_identity));
+ return auth->authorized_identity;
+ }
+}
+
+/**
+ * Gets the GUID from the server if we've authenticated; gets
+ * #NULL otherwise.
+ * @param auth the auth object
+ * @returns the GUID in ASCII hex format
+ */
+const char*
+_dbus_auth_get_guid_from_server (DBusAuth *auth)
+{
+ _dbus_assert (DBUS_AUTH_IS_CLIENT (auth));
+
+ if (auth->state == &common_state_authenticated)
+ return _dbus_string_get_const_data (& DBUS_AUTH_CLIENT (auth)->guid_from_server);
+ else
+ return NULL;
+}
+
+/**
+ * Sets the "authentication context" which scopes cookies
+ * with the DBUS_COOKIE_SHA1 auth mechanism for example.
+ *
+ * @param auth the auth conversation
+ * @param context the context
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_auth_set_context (DBusAuth *auth,
+ const DBusString *context)
+{
+ return _dbus_string_replace_len (context, 0, _dbus_string_get_length (context),
+ &auth->context, 0, _dbus_string_get_length (context));
+}
+
+/**
+ * Sets whether unix fd passing is potentially on the transport and
+ * hence shall be negotiated.
+ *
+ * @param auth the auth conversation
+ * @param b TRUE when unix fd passing shall be negotiated, otherwise FALSE
+ */
+void
+_dbus_auth_set_unix_fd_possible(DBusAuth *auth, dbus_bool_t b)
+{
+ auth->unix_fd_possible = b;
+}
+
+/**
+ * Queries whether unix fd passing was successfully negotiated.
+ *
+ * @param auth the auth conversion
+ * @returns #TRUE when unix fd passing was negotiated.
+ */
+dbus_bool_t
+_dbus_auth_get_unix_fd_negotiated(DBusAuth *auth)
+{
+ return auth->unix_fd_negotiated;
+}
+
+/**
+ * Queries whether the given auth mechanism is supported.
+ *
+ * @param auth the auth mechanism to query for
+ * @returns #TRUE when auth mechanism is supported
+ */
+dbus_bool_t
+_dbus_auth_is_supported_mechanism (DBusString *name)
+{
+ _dbus_assert (name != NULL);
+
+ return find_mech (name, NULL) != NULL;
+}
+
+/**
+ * Return a human-readable string containing all supported auth mechanisms.
+ *
+ * @param string to hold the supported auth mechanisms
+ * @returns #FALSE on oom
+ */
+dbus_bool_t
+_dbus_auth_dump_supported_mechanisms (DBusString *buffer)
+{
+ unsigned int i;
+ _dbus_assert (buffer != NULL);
+
+ for (i = 0; all_mechanisms[i].mechanism != NULL; i++)
+ {
+ if (i > 0)
+ {
+ if (!_dbus_string_append (buffer, ", "))
+ return FALSE;
+ }
+ if (!_dbus_string_append (buffer, all_mechanisms[i].mechanism))
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/** @} */
+
+/* tests in dbus-auth-util.c */
diff --git a/src/3rdparty/libdbus/dbus/dbus-auth.h b/src/3rdparty/libdbus/dbus/dbus-auth.h
new file mode 100644
index 00000000..4612d5a8
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-auth.h
@@ -0,0 +1,104 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-auth.h Authentication
+ *
+ * Copyright (C) 2002 Red Hat Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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
+ *
+ */
+#ifndef DBUS_AUTH_H
+#define DBUS_AUTH_H
+
+#include <dbus/dbus-macros.h>
+#include <dbus/dbus-errors.h>
+#include <dbus/dbus-string.h>
+#include <dbus/dbus-sysdeps.h>
+
+DBUS_BEGIN_DECLS
+
+typedef struct DBusAuth DBusAuth;
+
+typedef enum
+{
+ DBUS_AUTH_STATE_WAITING_FOR_INPUT,
+ DBUS_AUTH_STATE_WAITING_FOR_MEMORY,
+ DBUS_AUTH_STATE_HAVE_BYTES_TO_SEND,
+ DBUS_AUTH_STATE_NEED_DISCONNECT,
+ DBUS_AUTH_STATE_AUTHENTICATED,
+ DBUS_AUTH_STATE_INVALID = -1
+} DBusAuthState;
+
+DBUS_PRIVATE_EXPORT
+DBusAuth* _dbus_auth_server_new (const DBusString *guid);
+DBUS_PRIVATE_EXPORT
+DBusAuth* _dbus_auth_client_new (void);
+DBUS_PRIVATE_EXPORT
+DBusAuth* _dbus_auth_ref (DBusAuth *auth);
+DBUS_PRIVATE_EXPORT
+void _dbus_auth_unref (DBusAuth *auth);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_auth_set_mechanisms (DBusAuth *auth,
+ const char **mechanisms);
+DBUS_PRIVATE_EXPORT
+DBusAuthState _dbus_auth_do_work (DBusAuth *auth);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_auth_get_bytes_to_send (DBusAuth *auth,
+ const DBusString **str);
+DBUS_PRIVATE_EXPORT
+void _dbus_auth_bytes_sent (DBusAuth *auth,
+ int bytes_sent);
+DBUS_PRIVATE_EXPORT
+void _dbus_auth_get_buffer (DBusAuth *auth,
+ DBusString **buffer);
+DBUS_PRIVATE_EXPORT
+void _dbus_auth_return_buffer (DBusAuth *auth,
+ DBusString *buffer);
+DBUS_PRIVATE_EXPORT
+void _dbus_auth_get_unused_bytes (DBusAuth *auth,
+ const DBusString **str);
+DBUS_PRIVATE_EXPORT
+void _dbus_auth_delete_unused_bytes (DBusAuth *auth);
+dbus_bool_t _dbus_auth_needs_encoding (DBusAuth *auth);
+dbus_bool_t _dbus_auth_encode_data (DBusAuth *auth,
+ const DBusString *plaintext,
+ DBusString *encoded);
+dbus_bool_t _dbus_auth_needs_decoding (DBusAuth *auth);
+dbus_bool_t _dbus_auth_decode_data (DBusAuth *auth,
+ const DBusString *encoded,
+ DBusString *plaintext);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_auth_set_credentials (DBusAuth *auth,
+ DBusCredentials *credentials);
+DBUS_PRIVATE_EXPORT
+DBusCredentials* _dbus_auth_get_identity (DBusAuth *auth);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_auth_set_context (DBusAuth *auth,
+ const DBusString *context);
+const char* _dbus_auth_get_guid_from_server(DBusAuth *auth);
+
+void _dbus_auth_set_unix_fd_possible(DBusAuth *auth, dbus_bool_t b);
+dbus_bool_t _dbus_auth_get_unix_fd_negotiated(DBusAuth *auth);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_auth_is_supported_mechanism(DBusString *name);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_auth_dump_supported_mechanisms(DBusString *buffer);
+
+DBUS_END_DECLS
+
+#endif /* DBUS_AUTH_H */
diff --git a/src/3rdparty/libdbus/dbus/dbus-backtrace-win.c b/src/3rdparty/libdbus/dbus/dbus-backtrace-win.c
new file mode 100644
index 00000000..d9bc2dc4
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-backtrace-win.c
@@ -0,0 +1,213 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-backtrace-win.c Backtrace Generator
+ *
+ * Copyright 2004 Eric Poech
+ * Copyright 2004 Robert Shearman
+ * Copyright 2010 Patrick von Reth <patrick.vonreth@gmail.com>
+ * Copyright 2015 Ralf Habacker <ralf.habacker@freenet.de>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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-sysdeps.h"
+
+#if !defined (DBUS_DISABLE_ASSERT) || defined(DBUS_ENABLE_EMBEDDED_TESTS)
+
+#if defined(_MSC_VER) || defined(DBUS_WINCE)
+# ifdef BACKTRACES
+# undef BACKTRACES
+# endif
+#else
+# define BACKTRACES
+#endif
+
+#ifdef BACKTRACES
+#include "dbus-sysdeps-win.h"
+
+#include <stdio.h>
+#include <windows.h>
+#include <imagehlp.h>
+
+#define DPRINTF(fmt, ...) fprintf (stderr, fmt, ##__VA_ARGS__)
+
+#ifdef _MSC_VER
+#define BOOL int
+
+#define __i386__
+#endif
+
+static void dump_backtrace_for_thread (HANDLE hThread)
+{
+ ADDRESS old_address;
+ STACKFRAME sf;
+ CONTEXT context;
+ DWORD dwImageType;
+ int i = 0;
+
+ SymSetOptions (SYMOPT_UNDNAME | SYMOPT_LOAD_LINES);
+ SymInitialize (GetCurrentProcess (), NULL, TRUE);
+
+
+ /* can't use this function for current thread as GetThreadContext
+ * doesn't support getting context from current thread */
+ if (hThread == GetCurrentThread())
+ return;
+
+ DPRINTF ("Backtrace:\n");
+
+ _DBUS_ZERO (old_address);
+ _DBUS_ZERO (context);
+ context.ContextFlags = CONTEXT_FULL;
+
+ SuspendThread (hThread);
+
+ if (!GetThreadContext (hThread, &context))
+ {
+ DPRINTF ("Couldn't get thread context (error %ld)\n", GetLastError ());
+ ResumeThread (hThread);
+ return;
+ }
+
+ _DBUS_ZERO (sf);
+
+#ifdef __i386__
+ dwImageType = IMAGE_FILE_MACHINE_I386;
+ sf.AddrFrame.Offset = context.Ebp;
+ sf.AddrFrame.Mode = AddrModeFlat;
+ sf.AddrPC.Offset = context.Eip;
+ sf.AddrPC.Mode = AddrModeFlat;
+#elif defined(_M_X64)
+ dwImageType = IMAGE_FILE_MACHINE_AMD64;
+ sf.AddrPC.Offset = context.Rip;
+ sf.AddrPC.Mode = AddrModeFlat;
+ sf.AddrFrame.Offset = context.Rsp;
+ sf.AddrFrame.Mode = AddrModeFlat;
+ sf.AddrStack.Offset = context.Rsp;
+ sf.AddrStack.Mode = AddrModeFlat;
+#elif defined(_M_IA64)
+ dwImageType = IMAGE_FILE_MACHINE_IA64;
+ sf.AddrPC.Offset = context.StIIP;
+ sf.AddrPC.Mode = AddrModeFlat;
+ sf.AddrFrame.Offset = context.IntSp;
+ sf.AddrFrame.Mode = AddrModeFlat;
+ sf.AddrBStore.Offset= context.RsBSP;
+ sf.AddrBStore.Mode = AddrModeFlat;
+ sf.AddrStack.Offset = context.IntSp;
+ sf.AddrStack.Mode = AddrModeFlat;
+#else
+# error You need to fill in the STACKFRAME structure for your architecture
+#endif
+
+ /*
+ backtrace format
+ <level> <address> <symbol>[+offset] [ '[' <file> ':' <line> ']' ] [ 'in' <module> ]
+ example:
+ 6 0xf75ade6b wine_switch_to_stack+0x2a [/usr/src/debug/wine-snapshot/libs/wine/port.c:59] in libwine.so.1
+ */
+ while (StackWalk (dwImageType, GetCurrentProcess (),
+ hThread, &sf, &context, NULL, SymFunctionTableAccess,
+ SymGetModuleBase, NULL))
+ {
+ char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(char)];
+ PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)buffer;
+ DWORD64 displacement;
+ IMAGEHLP_LINE line;
+ DWORD dwDisplacement;
+ IMAGEHLP_MODULE moduleInfo;
+
+ /*
+ on Wine64 version 1.7.54, we get an infinite number of stack entries
+ pointing to the same stack frame (_start+0x29 in <wine-loader>)
+ see bug https://bugs.winehq.org/show_bug.cgi?id=39606
+ */
+#ifndef __i386__
+ if (old_address.Offset == sf.AddrPC.Offset)
+ {
+ break;
+ }
+#endif
+
+ pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
+ pSymbol->MaxNameLen = MAX_SYM_NAME;
+
+ if (SymFromAddr (GetCurrentProcess (), sf.AddrPC.Offset, &displacement, pSymbol))
+ {
+ if (displacement)
+ DPRINTF ("%3d %s+0x%I64x", i++, pSymbol->Name, displacement);
+ else
+ DPRINTF ("%3d %s", i++, pSymbol->Name);
+ }
+ else
+ DPRINTF ("%3d 0x%Ix", i++, sf.AddrPC.Offset);
+
+ line.SizeOfStruct = sizeof(IMAGEHLP_LINE);
+ if (SymGetLineFromAddr (GetCurrentProcess (), sf.AddrPC.Offset, &dwDisplacement, &line))
+ {
+ DPRINTF (" [%s:%ld]", line.FileName, line.LineNumber);
+ }
+
+ moduleInfo.SizeOfStruct = sizeof(moduleInfo);
+ if (SymGetModuleInfo (GetCurrentProcess (), sf.AddrPC.Offset, &moduleInfo))
+ {
+ DPRINTF (" in %s", moduleInfo.ModuleName);
+ }
+ DPRINTF ("\n");
+ old_address = sf.AddrPC;
+ }
+ ResumeThread (hThread);
+}
+
+static DWORD WINAPI dump_thread_proc (LPVOID lpParameter)
+{
+ dump_backtrace_for_thread ((HANDLE) lpParameter);
+ return 0;
+}
+
+/* cannot get valid context from current thread, so we have to execute
+ * backtrace from another thread */
+static void
+dump_backtrace (void)
+{
+ HANDLE hCurrentThread;
+ HANDLE hThread;
+ DWORD dwThreadId;
+ DuplicateHandle (GetCurrentProcess (), GetCurrentThread (),
+ GetCurrentProcess (), &hCurrentThread,
+ 0, FALSE, DUPLICATE_SAME_ACCESS);
+ hThread = CreateThread (NULL, 0, dump_thread_proc, (LPVOID)hCurrentThread,
+ 0, &dwThreadId);
+ WaitForSingleObject (hThread, INFINITE);
+ CloseHandle (hThread);
+ CloseHandle (hCurrentThread);
+}
+#endif
+#endif /* asserts or tests enabled */
+
+#ifdef BACKTRACES
+void _dbus_print_backtrace (void)
+{
+ dump_backtrace ();
+}
+#else
+void _dbus_print_backtrace (void)
+{
+ _dbus_verbose (" D-Bus not compiled with backtrace support\n");
+}
+#endif
diff --git a/src/3rdparty/libdbus/dbus/dbus-bus.c b/src/3rdparty/libdbus/dbus/dbus-bus.c
new file mode 100644
index 00000000..f0c790df
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-bus.c
@@ -0,0 +1,1605 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-bus.c Convenience functions for communicating with the bus.
+ *
+ * Copyright (C) 2003 CodeFactory AB
+ * Copyright (C) 2003 Red Hat, Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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-bus.h"
+#include "dbus-protocol.h"
+#include "dbus-internals.h"
+#include "dbus-message.h"
+#include "dbus-marshal-validate.h"
+#include "dbus-misc.h"
+#include "dbus-threads-internal.h"
+#include "dbus-connection-internal.h"
+#include "dbus-string.h"
+
+/**
+ * @defgroup DBusBus Message bus APIs
+ * @ingroup DBus
+ * @brief Functions for communicating with the message bus
+ *
+ * dbus_bus_get() allows all modules and libraries in a given
+ * process to share the same connection to the bus daemon by storing
+ * the connection globally.
+ *
+ * All other functions in this module are just convenience functions;
+ * most of them invoke methods on the bus daemon, by sending method
+ * call messages to #DBUS_SERVICE_DBUS. These convenience functions
+ * often make blocking method calls. If you don't want to block,
+ * you can send the method call messages manually in the same way
+ * you would any other method call message.
+ *
+ * This module is the only one in libdbus that's specific to
+ * communicating with the message bus daemon. The rest of the API can
+ * also be used for connecting to another application directly.
+ *
+ * @todo right now the default address of the system bus is hardcoded,
+ * so if you change it in the global config file suddenly you have to
+ * set DBUS_SYSTEM_BUS_ADDRESS env variable. Might be nice if the
+ * client lib somehow read the config file, or if the bus on startup
+ * somehow wrote out its address to a well-known spot, but might also
+ * not be worth it.
+ */
+
+/**
+ * @defgroup DBusBusInternals Message bus APIs internals
+ * @ingroup DBusInternals
+ * @brief Internals of functions for communicating with the message bus
+ *
+ * @{
+ */
+
+/**
+ * Block of message-bus-related data we attach to each
+ * #DBusConnection used with these convenience functions.
+ *
+ */
+typedef struct
+{
+ DBusConnection *connection; /**< Connection we're associated with */
+ char *unique_name; /**< Unique name of this connection */
+
+ unsigned int is_well_known : 1; /**< Is one of the well-known connections in our global array */
+} BusData;
+
+/** The slot we have reserved to store BusData.
+ * Protected by _DBUS_LOCK_connection_slots.
+ */
+static dbus_int32_t bus_data_slot = -1;
+
+/** Number of bus types */
+#define N_BUS_TYPES 3
+
+/* Protected by _DBUS_LOCK_bus, except during shutdown, which can't safely
+ * be done in a threaded application anyway. */
+static DBusConnection *bus_connections[N_BUS_TYPES];
+static char *bus_connection_addresses[N_BUS_TYPES] = { NULL, NULL, NULL };
+static DBusBusType activation_bus_type = DBUS_BUS_STARTER;
+static dbus_bool_t initialized = FALSE;
+
+static void
+addresses_shutdown_func (void *data)
+{
+ int i;
+
+ i = 0;
+ while (i < N_BUS_TYPES)
+ {
+ if (bus_connections[i] != NULL)
+ _dbus_warn_check_failed ("dbus_shutdown() called but connections were still live. This probably means the application did not drop all its references to bus connections.");
+
+ dbus_free (bus_connection_addresses[i]);
+ bus_connection_addresses[i] = NULL;
+ ++i;
+ }
+
+ activation_bus_type = DBUS_BUS_STARTER;
+
+ initialized = FALSE;
+}
+
+static dbus_bool_t
+get_from_env (char **connection_p,
+ const char *env_var)
+{
+ const char *s;
+
+ _dbus_assert (*connection_p == NULL);
+
+ s = _dbus_getenv (env_var);
+ if (s == NULL || *s == '\0')
+ return TRUE; /* successfully didn't use the env var */
+ else
+ {
+ *connection_p = _dbus_strdup (s);
+ return *connection_p != NULL;
+ }
+}
+
+static dbus_bool_t
+init_session_address (void)
+{
+ dbus_bool_t retval;
+
+ retval = FALSE;
+
+ /* First, look in the environment. This is the normal case on
+ * freedesktop.org/Unix systems. */
+ get_from_env (&bus_connection_addresses[DBUS_BUS_SESSION],
+ "DBUS_SESSION_BUS_ADDRESS");
+ if (bus_connection_addresses[DBUS_BUS_SESSION] == NULL)
+ {
+ dbus_bool_t supported;
+ DBusString addr;
+ DBusError error = DBUS_ERROR_INIT;
+
+ if (!_dbus_string_init (&addr))
+ return FALSE;
+
+ supported = FALSE;
+ /* So it's not in the environment - let's try a platform-specific method.
+ * On MacOS, this involves asking launchd. On Windows (not specified yet)
+ * we might do a COM lookup.
+ * Ignore errors - if we failed, fall back to autolaunch. */
+ retval = _dbus_lookup_session_address (&supported, &addr, &error);
+ if (supported && retval)
+ {
+ retval =_dbus_string_steal_data (&addr, &bus_connection_addresses[DBUS_BUS_SESSION]);
+ }
+ else if (supported && !retval)
+ {
+ if (dbus_error_is_set(&error))
+ _dbus_warn ("Dynamic session lookup supported but failed: %s", error.message);
+ else
+ _dbus_warn ("Dynamic session lookup supported but failed silently");
+ }
+ _dbus_string_free (&addr);
+ }
+ else
+ retval = TRUE;
+
+ if (!retval)
+ return FALSE;
+
+ /* We have a hard-coded (but compile-time-configurable) fallback address for
+ * the session bus. */
+ if (bus_connection_addresses[DBUS_BUS_SESSION] == NULL)
+ bus_connection_addresses[DBUS_BUS_SESSION] =
+ _dbus_strdup (DBUS_SESSION_BUS_CONNECT_ADDRESS);
+
+ if (bus_connection_addresses[DBUS_BUS_SESSION] == NULL)
+ return FALSE;
+
+ return TRUE;
+}
+
+static dbus_bool_t
+init_connections_unlocked (void)
+{
+ if (!initialized)
+ {
+ const char *s;
+ int i;
+
+ i = 0;
+ while (i < N_BUS_TYPES)
+ {
+ bus_connections[i] = NULL;
+ ++i;
+ }
+
+ /* Don't init these twice, we may run this code twice if
+ * init_connections_unlocked() fails midway through.
+ * In practice, each block below should contain only one
+ * "return FALSE" or running through twice may not
+ * work right.
+ */
+
+ if (bus_connection_addresses[DBUS_BUS_SYSTEM] == NULL)
+ {
+ _dbus_verbose ("Filling in system bus address...\n");
+
+ if (!get_from_env (&bus_connection_addresses[DBUS_BUS_SYSTEM],
+ "DBUS_SYSTEM_BUS_ADDRESS"))
+ return FALSE;
+ }
+
+
+ if (bus_connection_addresses[DBUS_BUS_SYSTEM] == NULL)
+ {
+ /* Use default system bus address if none set in environment */
+ bus_connection_addresses[DBUS_BUS_SYSTEM] =
+ _dbus_strdup (DBUS_SYSTEM_BUS_DEFAULT_ADDRESS);
+
+ if (bus_connection_addresses[DBUS_BUS_SYSTEM] == NULL)
+ return FALSE;
+
+ _dbus_verbose (" used default system bus \"%s\"\n",
+ bus_connection_addresses[DBUS_BUS_SYSTEM]);
+ }
+ else
+ _dbus_verbose (" used env var system bus \"%s\"\n",
+ bus_connection_addresses[DBUS_BUS_SYSTEM]);
+
+ if (bus_connection_addresses[DBUS_BUS_SESSION] == NULL)
+ {
+ _dbus_verbose ("Filling in session bus address...\n");
+
+ if (!init_session_address ())
+ return FALSE;
+
+ _dbus_verbose (" \"%s\"\n", bus_connection_addresses[DBUS_BUS_SESSION] ?
+ bus_connection_addresses[DBUS_BUS_SESSION] : "none set");
+ }
+
+ if (bus_connection_addresses[DBUS_BUS_STARTER] == NULL)
+ {
+ _dbus_verbose ("Filling in activation bus address...\n");
+
+ if (!get_from_env (&bus_connection_addresses[DBUS_BUS_STARTER],
+ "DBUS_STARTER_ADDRESS"))
+ return FALSE;
+
+ _dbus_verbose (" \"%s\"\n", bus_connection_addresses[DBUS_BUS_STARTER] ?
+ bus_connection_addresses[DBUS_BUS_STARTER] : "none set");
+ }
+
+
+ if (bus_connection_addresses[DBUS_BUS_STARTER] != NULL)
+ {
+ s = _dbus_getenv ("DBUS_STARTER_BUS_TYPE");
+
+ if (s != NULL)
+ {
+ _dbus_verbose ("Bus activation type was set to \"%s\"\n", s);
+
+ if (strcmp (s, "system") == 0)
+ activation_bus_type = DBUS_BUS_SYSTEM;
+ else if (strcmp (s, "session") == 0)
+ activation_bus_type = DBUS_BUS_SESSION;
+ }
+ }
+ else
+ {
+ /* Default to the session bus instead if available */
+ if (bus_connection_addresses[DBUS_BUS_SESSION] != NULL)
+ {
+ bus_connection_addresses[DBUS_BUS_STARTER] =
+ _dbus_strdup (bus_connection_addresses[DBUS_BUS_SESSION]);
+ if (bus_connection_addresses[DBUS_BUS_STARTER] == NULL)
+ return FALSE;
+ }
+ }
+
+ /* If we return FALSE we have to be sure that restarting
+ * the above code will work right
+ */
+
+ if (!_dbus_register_shutdown_func (addresses_shutdown_func,
+ NULL))
+ return FALSE;
+
+ initialized = TRUE;
+ }
+
+ return initialized;
+}
+
+static void
+bus_data_free (void *data)
+{
+ BusData *bd = data;
+
+ if (bd->is_well_known)
+ {
+ int i;
+
+ if (!_DBUS_LOCK (bus))
+ _dbus_assert_not_reached ("global locks should have been initialized "
+ "when we attached bus data");
+
+ /* We may be stored in more than one slot */
+ /* This should now be impossible - these slots are supposed to
+ * be cleared on disconnect, so should not need to be cleared on
+ * finalize
+ */
+ i = 0;
+ while (i < N_BUS_TYPES)
+ {
+ if (bus_connections[i] == bd->connection)
+ bus_connections[i] = NULL;
+
+ ++i;
+ }
+ _DBUS_UNLOCK (bus);
+ }
+
+ dbus_free (bd->unique_name);
+ dbus_free (bd);
+
+ dbus_connection_free_data_slot (&bus_data_slot);
+}
+
+static BusData*
+ensure_bus_data (DBusConnection *connection)
+{
+ BusData *bd;
+
+ if (!dbus_connection_allocate_data_slot (&bus_data_slot))
+ return NULL;
+
+ bd = dbus_connection_get_data (connection, bus_data_slot);
+ if (bd == NULL)
+ {
+ bd = dbus_new0 (BusData, 1);
+ if (bd == NULL)
+ {
+ dbus_connection_free_data_slot (&bus_data_slot);
+ return NULL;
+ }
+
+ bd->connection = connection;
+
+ if (!dbus_connection_set_data (connection, bus_data_slot, bd,
+ bus_data_free))
+ {
+ dbus_free (bd);
+ dbus_connection_free_data_slot (&bus_data_slot);
+ return NULL;
+ }
+
+ /* Data slot refcount now held by the BusData */
+ }
+ else
+ {
+ dbus_connection_free_data_slot (&bus_data_slot);
+ }
+
+ return bd;
+}
+
+/**
+ * Internal function that checks to see if this
+ * is a shared connection owned by the bus and if it is unref it.
+ *
+ * @param connection a connection that has been disconnected.
+ */
+void
+_dbus_bus_notify_shared_connection_disconnected_unlocked (DBusConnection *connection)
+{
+ int i;
+
+ if (!_DBUS_LOCK (bus))
+ {
+ /* If it was in bus_connections, we would have initialized global locks
+ * when we added it. So, it can't be. */
+ return;
+ }
+
+ /* We are expecting to have the connection saved in only one of these
+ * slots, but someone could in a pathological case set system and session
+ * bus to the same bus or something. Or set one of them to the starter
+ * bus without setting the starter bus type in the env variable.
+ * So we don't break the loop as soon as we find a match.
+ */
+ for (i = 0; i < N_BUS_TYPES; ++i)
+ {
+ if (bus_connections[i] == connection)
+ {
+ bus_connections[i] = NULL;
+ }
+ }
+
+ _DBUS_UNLOCK (bus);
+}
+
+static DBusConnection *
+internal_bus_get (DBusBusType type,
+ dbus_bool_t private,
+ DBusError *error)
+{
+ const char *address;
+ DBusConnection *connection;
+ BusData *bd;
+ DBusBusType address_type;
+
+ _dbus_return_val_if_fail (type >= 0 && type < N_BUS_TYPES, NULL);
+ _dbus_return_val_if_error_is_set (error, NULL);
+
+ connection = NULL;
+
+ if (!_DBUS_LOCK (bus))
+ {
+ _DBUS_SET_OOM (error);
+ /* do not "goto out", that would try to unlock */
+ return NULL;
+ }
+
+ if (!init_connections_unlocked ())
+ {
+ _DBUS_SET_OOM (error);
+ goto out;
+ }
+
+ /* We want to use the activation address even if the
+ * activating bus is the session or system bus,
+ * per the spec.
+ */
+ address_type = type;
+
+ /* Use the real type of the activation bus for getting its
+ * connection, but only if the real type's address is available. (If
+ * the activating bus isn't a well-known bus then
+ * activation_bus_type == DBUS_BUS_STARTER)
+ */
+ if (type == DBUS_BUS_STARTER &&
+ bus_connection_addresses[activation_bus_type] != NULL)
+ type = activation_bus_type;
+
+ if (!private && bus_connections[type] != NULL)
+ {
+ connection = bus_connections[type];
+ dbus_connection_ref (connection);
+ goto out;
+ }
+
+ address = bus_connection_addresses[address_type];
+ if (address == NULL)
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "Unable to determine the address of the message bus (try 'man dbus-launch' and 'man dbus-daemon' for help)");
+ goto out;
+ }
+
+ if (private)
+ connection = dbus_connection_open_private (address, error);
+ else
+ connection = dbus_connection_open (address, error);
+
+ if (!connection)
+ {
+ goto out;
+ }
+
+ if (!dbus_bus_register (connection, error))
+ {
+ _dbus_connection_close_possibly_shared (connection);
+ dbus_connection_unref (connection);
+ connection = NULL;
+ goto out;
+ }
+
+ if (!private)
+ {
+ /* store a weak ref to the connection (dbus-connection.c is
+ * supposed to have a strong ref that it drops on disconnect,
+ * since this is a shared connection)
+ */
+ bus_connections[type] = connection;
+ }
+
+ /* By default we're bound to the lifecycle of
+ * the message bus.
+ */
+ dbus_connection_set_exit_on_disconnect (connection,
+ TRUE);
+
+ if (!_DBUS_LOCK (bus_datas))
+ _dbus_assert_not_reached ("global locks were initialized already");
+
+ bd = ensure_bus_data (connection);
+ _dbus_assert (bd != NULL); /* it should have been created on
+ register, so OOM not possible */
+ bd->is_well_known = TRUE;
+ _DBUS_UNLOCK (bus_datas);
+
+out:
+ /* Return a reference to the caller, or NULL with error set. */
+ if (connection == NULL)
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+
+ _DBUS_UNLOCK (bus);
+ return connection;
+}
+
+
+/** @} */ /* end of implementation details docs */
+
+/**
+ * @addtogroup DBusBus
+ * @{
+ */
+
+/**
+ * Connects to a bus daemon and registers the client with it. If a
+ * connection to the bus already exists, then that connection is
+ * returned. The caller of this function owns a reference to the bus.
+ *
+ * The caller may NOT call dbus_connection_close() on this connection;
+ * see dbus_connection_open() and dbus_connection_close() for details
+ * on that.
+ *
+ * If this function obtains a new connection object never before
+ * returned from dbus_bus_get(), it will call
+ * dbus_connection_set_exit_on_disconnect(), so the application
+ * will exit if the connection closes. You can undo this
+ * by calling dbus_connection_set_exit_on_disconnect() yourself
+ * after you get the connection.
+ *
+ * dbus_bus_get() calls dbus_bus_register() for you.
+ *
+ * If returning a newly-created connection, this function will block
+ * until authentication and bus registration are complete.
+ *
+ * @param type bus type
+ * @param error address where an error can be returned.
+ * @returns a #DBusConnection with new ref or #NULL on error
+ */
+DBusConnection *
+dbus_bus_get (DBusBusType type,
+ DBusError *error)
+{
+ return internal_bus_get (type, FALSE, error);
+}
+
+/**
+ * Connects to a bus daemon and registers the client with it as with
+ * dbus_bus_register(). Unlike dbus_bus_get(), always creates a new
+ * connection. This connection will not be saved or recycled by
+ * libdbus. Caller owns a reference to the bus and must either close
+ * it or know it to be closed prior to releasing this reference.
+ *
+ * See dbus_connection_open_private() for more details on when to
+ * close and unref this connection.
+ *
+ * This function calls
+ * dbus_connection_set_exit_on_disconnect() on the new connection, so the application
+ * will exit if the connection closes. You can undo this
+ * by calling dbus_connection_set_exit_on_disconnect() yourself
+ * after you get the connection.
+ *
+ * dbus_bus_get_private() calls dbus_bus_register() for you.
+ *
+ * This function will block until authentication and bus registration
+ * are complete.
+ *
+ * @param type bus type
+ * @param error address where an error can be returned.
+ * @returns a DBusConnection with new ref
+ */
+DBusConnection *
+dbus_bus_get_private (DBusBusType type,
+ DBusError *error)
+{
+ return internal_bus_get (type, TRUE, error);
+}
+
+/**
+ * Registers a connection with the bus. This must be the first
+ * thing an application does when connecting to the message bus.
+ * If registration succeeds, the unique name will be set,
+ * and can be obtained using dbus_bus_get_unique_name().
+ *
+ * This function will block until registration is complete.
+ *
+ * If the connection has already registered with the bus
+ * (determined by checking whether dbus_bus_get_unique_name()
+ * returns a non-#NULL value), then this function does nothing.
+ *
+ * If you use dbus_bus_get() or dbus_bus_get_private() this
+ * function will be called for you.
+ *
+ * @note Just use dbus_bus_get() or dbus_bus_get_private() instead of
+ * dbus_bus_register() and save yourself some pain. Using
+ * dbus_bus_register() manually is only useful if you have your
+ * own custom message bus not found in #DBusBusType.
+ *
+ * If you open a bus connection with dbus_connection_open() or
+ * dbus_connection_open_private() you will have to dbus_bus_register()
+ * yourself, or make the appropriate registration method calls
+ * yourself. If you send the method calls yourself, call
+ * dbus_bus_set_unique_name() with the unique bus name you get from
+ * the bus.
+ *
+ * For shared connections (created with dbus_connection_open()) in a
+ * multithreaded application, you can't really make the registration
+ * calls yourself, because you don't know whether some other thread is
+ * also registering, and the bus will kick you off if you send two
+ * registration messages.
+ *
+ * If you use dbus_bus_register() however, there is a lock that
+ * keeps both apps from registering at the same time.
+ *
+ * The rule in a multithreaded app, then, is that dbus_bus_register()
+ * must be used to register, or you need to have your own locks that
+ * all threads in the app will respect.
+ *
+ * In a single-threaded application you can register by hand instead
+ * of using dbus_bus_register(), as long as you check
+ * dbus_bus_get_unique_name() to see if a unique name has already been
+ * stored by another thread before you send the registration messages.
+ *
+ * @param connection the connection
+ * @param error place to store errors
+ * @returns #TRUE on success
+ */
+dbus_bool_t
+dbus_bus_register (DBusConnection *connection,
+ DBusError *error)
+{
+ DBusMessage *message, *reply;
+ char *name;
+ BusData *bd;
+ dbus_bool_t retval;
+
+ _dbus_return_val_if_fail (connection != NULL, FALSE);
+ _dbus_return_val_if_error_is_set (error, FALSE);
+
+ retval = FALSE;
+ message = NULL;
+ reply = NULL;
+
+ if (!_DBUS_LOCK (bus_datas))
+ {
+ _DBUS_SET_OOM (error);
+ /* do not "goto out", that would try to unlock */
+ return FALSE;
+ }
+
+ bd = ensure_bus_data (connection);
+ if (bd == NULL)
+ {
+ _DBUS_SET_OOM (error);
+ goto out;
+ }
+
+ if (bd->unique_name != NULL)
+ {
+ _dbus_verbose ("Ignoring attempt to register the same DBusConnection %s with the message bus a second time.\n",
+ bd->unique_name);
+ /* Success! */
+ retval = TRUE;
+ goto out;
+ }
+
+ message = dbus_message_new_method_call (DBUS_SERVICE_DBUS,
+ DBUS_PATH_DBUS,
+ DBUS_INTERFACE_DBUS,
+ "Hello");
+
+ if (!message)
+ {
+ _DBUS_SET_OOM (error);
+ goto out;
+ }
+
+ reply = dbus_connection_send_with_reply_and_block (connection, message, -1, error);
+
+ if (reply == NULL)
+ goto out;
+ else if (dbus_set_error_from_message (error, reply))
+ goto out;
+ else if (!dbus_message_get_args (reply, error,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_INVALID))
+ goto out;
+
+ bd->unique_name = _dbus_strdup (name);
+ if (bd->unique_name == NULL)
+ {
+ _DBUS_SET_OOM (error);
+ goto out;
+ }
+
+ retval = TRUE;
+
+ out:
+ _DBUS_UNLOCK (bus_datas);
+
+ if (message)
+ dbus_message_unref (message);
+
+ if (reply)
+ dbus_message_unref (reply);
+
+ if (!retval)
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+
+ return retval;
+}
+
+
+/**
+ * Sets the unique name of the connection, as assigned by the message
+ * bus. Can only be used if you registered with the bus manually
+ * (i.e. if you did not call dbus_bus_register()). Can only be called
+ * once per connection. After the unique name is set, you can get it
+ * with dbus_bus_get_unique_name().
+ *
+ * The only reason to use this function is to re-implement the
+ * equivalent of dbus_bus_register() yourself. One (probably unusual)
+ * reason to do that might be to do the bus registration call
+ * asynchronously instead of synchronously.
+ *
+ * @note Just use dbus_bus_get() or dbus_bus_get_private(), or worst
+ * case dbus_bus_register(), instead of messing with this
+ * function. There's really no point creating pain for yourself by
+ * doing things manually.
+ *
+ * It's hard to use this function safely on shared connections
+ * (created by dbus_connection_open()) in a multithreaded application,
+ * because only one registration attempt can be sent to the bus. If
+ * two threads are both sending the registration message, there is no
+ * mechanism in libdbus itself to avoid sending it twice.
+ *
+ * Thus, you need a way to coordinate which thread sends the
+ * registration attempt; which also means you know which thread
+ * will call dbus_bus_set_unique_name(). If you don't know
+ * about all threads in the app (for example, if some libraries
+ * you're using might start libdbus-using threads), then you
+ * need to avoid using this function on shared connections.
+ *
+ * @param connection the connection
+ * @param unique_name the unique name
+ * @returns #FALSE if not enough memory
+ */
+dbus_bool_t
+dbus_bus_set_unique_name (DBusConnection *connection,
+ const char *unique_name)
+{
+ BusData *bd;
+ dbus_bool_t success = FALSE;
+
+ _dbus_return_val_if_fail (connection != NULL, FALSE);
+ _dbus_return_val_if_fail (unique_name != NULL, FALSE);
+
+ if (!_DBUS_LOCK (bus_datas))
+ {
+ /* do not "goto out", that would try to unlock */
+ return FALSE;
+ }
+
+ bd = ensure_bus_data (connection);
+ if (bd == NULL)
+ goto out;
+
+ _dbus_assert (bd->unique_name == NULL);
+
+ bd->unique_name = _dbus_strdup (unique_name);
+ success = bd->unique_name != NULL;
+
+out:
+ _DBUS_UNLOCK (bus_datas);
+
+ return success;
+}
+
+/**
+ * Gets the unique name of the connection as assigned by the message
+ * bus. Only possible after the connection has been registered with
+ * the message bus. All connections returned by dbus_bus_get() or
+ * dbus_bus_get_private() have been successfully registered.
+ *
+ * The name remains valid until the connection is freed, and
+ * should not be freed by the caller.
+ *
+ * Other than dbus_bus_get(), there are two ways to set the unique
+ * name; one is dbus_bus_register(), the other is
+ * dbus_bus_set_unique_name(). You are responsible for calling
+ * dbus_bus_set_unique_name() if you register by hand instead of using
+ * dbus_bus_register().
+ *
+ * @param connection the connection
+ * @returns the unique name or #NULL on error
+ */
+const char*
+dbus_bus_get_unique_name (DBusConnection *connection)
+{
+ BusData *bd;
+ const char *unique_name = NULL;
+
+ _dbus_return_val_if_fail (connection != NULL, NULL);
+
+ if (!_DBUS_LOCK (bus_datas))
+ {
+ /* We'd have initialized locks when we gave it its unique name, if it
+ * had one. Don't "goto out", that would try to unlock. */
+ return NULL;
+ }
+
+ bd = ensure_bus_data (connection);
+ if (bd == NULL)
+ goto out;
+
+ unique_name = bd->unique_name;
+
+out:
+ _DBUS_UNLOCK (bus_datas);
+
+ return unique_name;
+}
+
+/**
+ * Asks the bus to return the UID the named connection authenticated
+ * as, if any. Only works on UNIX; only works for connections on the
+ * same machine as the bus. If you are not on the same machine as the
+ * bus, then calling this is probably a bad idea, since the UID will
+ * mean little to your application.
+ *
+ * For the system message bus you're guaranteed to be on the same
+ * machine since it only listens on a UNIX domain socket (at least,
+ * as shipped by default).
+ *
+ * This function only works for connections that authenticated as
+ * a UNIX user, right now that includes all bus connections, but
+ * it's very possible to have connections with no associated UID.
+ * So check for errors and do something sensible if they happen.
+ *
+ * This function will always return an error on Windows.
+ *
+ * @param connection the connection
+ * @param name a name owned by the connection
+ * @param error location to store the error
+ * @returns the unix user id, or ((unsigned)-1) if error is set
+ */
+unsigned long
+dbus_bus_get_unix_user (DBusConnection *connection,
+ const char *name,
+ DBusError *error)
+{
+ DBusMessage *message, *reply;
+ dbus_uint32_t uid;
+
+ _dbus_return_val_if_fail (connection != NULL, DBUS_UID_UNSET);
+ _dbus_return_val_if_fail (name != NULL, DBUS_UID_UNSET);
+ _dbus_return_val_if_fail (_dbus_check_is_valid_bus_name (name), DBUS_UID_UNSET);
+ _dbus_return_val_if_error_is_set (error, DBUS_UID_UNSET);
+
+ message = dbus_message_new_method_call (DBUS_SERVICE_DBUS,
+ DBUS_PATH_DBUS,
+ DBUS_INTERFACE_DBUS,
+ "GetConnectionUnixUser");
+
+ if (message == NULL)
+ {
+ _DBUS_SET_OOM (error);
+ return DBUS_UID_UNSET;
+ }
+
+ if (!dbus_message_append_args (message,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_INVALID))
+ {
+ dbus_message_unref (message);
+ _DBUS_SET_OOM (error);
+ return DBUS_UID_UNSET;
+ }
+
+ reply = dbus_connection_send_with_reply_and_block (connection, message, -1,
+ error);
+
+ dbus_message_unref (message);
+
+ if (reply == NULL)
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ return DBUS_UID_UNSET;
+ }
+
+ if (dbus_set_error_from_message (error, reply))
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ dbus_message_unref (reply);
+ return DBUS_UID_UNSET;
+ }
+
+ if (!dbus_message_get_args (reply, error,
+ DBUS_TYPE_UINT32, &uid,
+ DBUS_TYPE_INVALID))
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ dbus_message_unref (reply);
+ return DBUS_UID_UNSET;
+ }
+
+ dbus_message_unref (reply);
+
+ return (unsigned long) uid;
+}
+
+/**
+ * Asks the bus to return its globally unique ID, as described in the
+ * D-Bus specification. For the session bus, this is useful as a way
+ * to uniquely identify each user session. For the system bus,
+ * probably the bus ID is not useful; instead, use the machine ID
+ * since it's accessible without necessarily connecting to the bus and
+ * may be persistent beyond a single bus instance (across reboots for
+ * example). See dbus_try_get_local_machine_id().
+ *
+ * In addition to an ID for each bus and an ID for each machine, there is
+ * an ID for each address that the bus is listening on; that can
+ * be retrieved with dbus_connection_get_server_id(), though it is
+ * probably not very useful.
+ *
+ * @param connection the connection
+ * @param error location to store the error
+ * @returns the bus ID or #NULL if error is set
+ */
+char*
+dbus_bus_get_id (DBusConnection *connection,
+ DBusError *error)
+{
+ DBusMessage *message, *reply;
+ char *id;
+ const char *v_STRING;
+
+ _dbus_return_val_if_fail (connection != NULL, NULL);
+ _dbus_return_val_if_error_is_set (error, NULL);
+
+ message = dbus_message_new_method_call (DBUS_SERVICE_DBUS,
+ DBUS_PATH_DBUS,
+ DBUS_INTERFACE_DBUS,
+ "GetId");
+
+ if (message == NULL)
+ {
+ _DBUS_SET_OOM (error);
+ return NULL;
+ }
+
+ reply = dbus_connection_send_with_reply_and_block (connection, message, -1,
+ error);
+
+ dbus_message_unref (message);
+
+ if (reply == NULL)
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ return NULL;
+ }
+
+ if (dbus_set_error_from_message (error, reply))
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ dbus_message_unref (reply);
+ return NULL;
+ }
+
+ v_STRING = NULL;
+ if (!dbus_message_get_args (reply, error,
+ DBUS_TYPE_STRING, &v_STRING,
+ DBUS_TYPE_INVALID))
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ dbus_message_unref (reply);
+ return NULL;
+ }
+
+ id = _dbus_strdup (v_STRING); /* may be NULL */
+
+ dbus_message_unref (reply);
+
+ if (id == NULL)
+ _DBUS_SET_OOM (error);
+
+ /* FIXME it might be nice to cache the ID locally */
+
+ return id;
+}
+
+/**
+ * Asks the bus to assign the given name to this connection by invoking
+ * the RequestName method on the bus. This method is fully documented
+ * in the D-Bus specification. For quick reference, the flags and
+ * result codes are discussed here, but the specification is the
+ * canonical version of this information.
+ *
+ * First you should know that for each bus name, the bus stores
+ * a queue of connections that would like to own it. Only
+ * one owns it at a time - called the primary owner. If the primary
+ * owner releases the name or disconnects, then the next owner in the
+ * queue atomically takes over.
+ *
+ * So for example if you have an application org.freedesktop.TextEditor
+ * and multiple instances of it can be run, you can have all of them
+ * sitting in the queue. The first one to start up will receive messages
+ * sent to org.freedesktop.TextEditor, but if that one exits another
+ * will become the primary owner and receive messages.
+ *
+ * The queue means you don't need to manually watch for the current owner to
+ * disappear and then request the name again.
+ *
+ * When requesting a name, you can specify several flags.
+ *
+ * #DBUS_NAME_FLAG_ALLOW_REPLACEMENT and #DBUS_NAME_FLAG_DO_NOT_QUEUE
+ * are properties stored by the bus for this connection with respect to
+ * each requested bus name. These properties are stored even if the
+ * connection is queued and does not become the primary owner.
+ * You can update these flags by calling RequestName again (even if
+ * you already own the name).
+ *
+ * #DBUS_NAME_FLAG_ALLOW_REPLACEMENT means that another requestor of the
+ * name can take it away from you by specifying #DBUS_NAME_FLAG_REPLACE_EXISTING.
+ *
+ * #DBUS_NAME_FLAG_DO_NOT_QUEUE means that if you aren't the primary owner,
+ * you don't want to be queued up - you only care about being the
+ * primary owner.
+ *
+ * Unlike the other two flags, #DBUS_NAME_FLAG_REPLACE_EXISTING is a property
+ * of the individual RequestName call, i.e. the bus does not persistently
+ * associate it with the connection-name pair. If a RequestName call includes
+ * the #DBUS_NAME_FLAG_REPLACE_EXISTING flag, and the current primary
+ * owner has #DBUS_NAME_FLAG_ALLOW_REPLACEMENT set, then the current primary
+ * owner will be kicked off.
+ *
+ * If no flags are given, an application will receive the requested
+ * name only if the name is currently unowned; and it will NOT give
+ * up the name if another application asks to take it over using
+ * #DBUS_NAME_FLAG_REPLACE_EXISTING.
+ *
+ * This function returns a result code. The possible result codes
+ * are as follows.
+ *
+ * #DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER means that the name had no
+ * existing owner, and the caller is now the primary owner; or that
+ * the name had an owner, and the caller specified
+ * #DBUS_NAME_FLAG_REPLACE_EXISTING, and the current owner
+ * specified #DBUS_NAME_FLAG_ALLOW_REPLACEMENT.
+ *
+ * #DBUS_REQUEST_NAME_REPLY_IN_QUEUE happens only if the caller does NOT
+ * specify #DBUS_NAME_FLAG_DO_NOT_QUEUE and either the current owner
+ * did NOT specify #DBUS_NAME_FLAG_ALLOW_REPLACEMENT or the caller did NOT
+ * specify #DBUS_NAME_FLAG_REPLACE_EXISTING. In this case the caller ends up
+ * in a queue to own the name after the current owner gives it up.
+ *
+ * #DBUS_REQUEST_NAME_REPLY_EXISTS happens if the name has an owner
+ * already and the caller specifies #DBUS_NAME_FLAG_DO_NOT_QUEUE
+ * and either the current owner has NOT specified
+ * #DBUS_NAME_FLAG_ALLOW_REPLACEMENT or the caller did NOT specify
+ * #DBUS_NAME_FLAG_REPLACE_EXISTING.
+ *
+ * #DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER happens if an application
+ * requests a name it already owns. (Re-requesting a name is useful if
+ * you want to change the #DBUS_NAME_FLAG_ALLOW_REPLACEMENT or
+ * #DBUS_NAME_FLAG_DO_NOT_QUEUE settings.)
+ *
+ * When a service represents an application, say "text editor," then
+ * it should specify #DBUS_NAME_FLAG_ALLOW_REPLACEMENT if it wants
+ * the last editor started to be the user's editor vs. the first one
+ * started. Then any editor that can be the user's editor should
+ * specify #DBUS_NAME_FLAG_REPLACE_EXISTING to either take over
+ * (last-started-wins) or be queued up (first-started-wins) according
+ * to whether #DBUS_NAME_FLAG_ALLOW_REPLACEMENT was given.
+ *
+ * Conventionally, single-instance applications often offer a command
+ * line option called --replace which means to replace the current
+ * instance. To implement this, always set
+ * #DBUS_NAME_FLAG_ALLOW_REPLACEMENT when you request your
+ * application's bus name. When you lose ownership of your bus name,
+ * you need to exit. Look for the signal "NameLost" from
+ * #DBUS_SERVICE_DBUS and #DBUS_INTERFACE_DBUS (the signal's first
+ * argument is the bus name that was lost). If starting up without
+ * --replace, do not specify #DBUS_NAME_FLAG_REPLACE_EXISTING, and
+ * exit if you fail to become the bus name owner. If --replace is
+ * given, ask to replace the old owner.
+ *
+ * @param connection the connection
+ * @param name the name to request
+ * @param flags flags
+ * @param error location to store the error
+ * @returns a result code, -1 if error is set
+ */
+int
+dbus_bus_request_name (DBusConnection *connection,
+ const char *name,
+ unsigned int flags,
+ DBusError *error)
+{
+ DBusMessage *message, *reply;
+ dbus_uint32_t result;
+
+ _dbus_return_val_if_fail (connection != NULL, 0);
+ _dbus_return_val_if_fail (name != NULL, 0);
+ _dbus_return_val_if_fail (_dbus_check_is_valid_bus_name (name), 0);
+ _dbus_return_val_if_error_is_set (error, 0);
+
+ message = dbus_message_new_method_call (DBUS_SERVICE_DBUS,
+ DBUS_PATH_DBUS,
+ DBUS_INTERFACE_DBUS,
+ "RequestName");
+
+ if (message == NULL)
+ {
+ _DBUS_SET_OOM (error);
+ return -1;
+ }
+
+ if (!dbus_message_append_args (message,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_UINT32, &flags,
+ DBUS_TYPE_INVALID))
+ {
+ dbus_message_unref (message);
+ _DBUS_SET_OOM (error);
+ return -1;
+ }
+
+ reply = dbus_connection_send_with_reply_and_block (connection, message, -1,
+ error);
+
+ dbus_message_unref (message);
+
+ if (reply == NULL)
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ return -1;
+ }
+
+ if (dbus_set_error_from_message (error, reply))
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ dbus_message_unref (reply);
+ return -1;
+ }
+
+ if (!dbus_message_get_args (reply, error,
+ DBUS_TYPE_UINT32, &result,
+ DBUS_TYPE_INVALID))
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ dbus_message_unref (reply);
+ return -1;
+ }
+
+ dbus_message_unref (reply);
+
+ return result;
+}
+
+
+/**
+ * Asks the bus to unassign the given name from this connection by
+ * invoking the ReleaseName method on the bus. The "ReleaseName"
+ * method is canonically documented in the D-Bus specification.
+ *
+ * Possible results are: #DBUS_RELEASE_NAME_REPLY_RELEASED
+ * which means you owned the name or were in the queue to own it,
+ * and and now you don't own it and aren't in the queue.
+ * #DBUS_RELEASE_NAME_REPLY_NOT_OWNER which means someone else
+ * owns the name so you can't release it.
+ * #DBUS_RELEASE_NAME_REPLY_NON_EXISTENT
+ * which means nobody owned the name.
+ *
+ * @param connection the connection
+ * @param name the name to remove
+ * @param error location to store the error
+ * @returns a result code, -1 if error is set
+ */
+int
+dbus_bus_release_name (DBusConnection *connection,
+ const char *name,
+ DBusError *error)
+{
+ DBusMessage *message, *reply;
+ dbus_uint32_t result;
+
+ _dbus_return_val_if_fail (connection != NULL, 0);
+ _dbus_return_val_if_fail (name != NULL, 0);
+ _dbus_return_val_if_fail (_dbus_check_is_valid_bus_name (name), 0);
+ _dbus_return_val_if_error_is_set (error, 0);
+
+ message = dbus_message_new_method_call (DBUS_SERVICE_DBUS,
+ DBUS_PATH_DBUS,
+ DBUS_INTERFACE_DBUS,
+ "ReleaseName");
+
+ if (message == NULL)
+ {
+ _DBUS_SET_OOM (error);
+ return -1;
+ }
+
+ if (!dbus_message_append_args (message,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_INVALID))
+ {
+ dbus_message_unref (message);
+ _DBUS_SET_OOM (error);
+ return -1;
+ }
+
+ reply = dbus_connection_send_with_reply_and_block (connection, message, -1,
+ error);
+
+ dbus_message_unref (message);
+
+ if (reply == NULL)
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ return -1;
+ }
+
+ if (dbus_set_error_from_message (error, reply))
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ dbus_message_unref (reply);
+ return -1;
+ }
+
+ if (!dbus_message_get_args (reply, error,
+ DBUS_TYPE_UINT32, &result,
+ DBUS_TYPE_INVALID))
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ dbus_message_unref (reply);
+ return -1;
+ }
+
+ dbus_message_unref (reply);
+
+ return result;
+}
+
+/**
+ * Asks the bus whether a certain name has an owner.
+ *
+ * Using this can easily result in a race condition,
+ * since an owner can appear or disappear after you
+ * call this.
+ *
+ * If you want to request a name, just request it;
+ * if you want to avoid replacing a current owner,
+ * don't specify #DBUS_NAME_FLAG_REPLACE_EXISTING and
+ * you will get an error if there's already an owner.
+ *
+ * @param connection the connection
+ * @param name the name
+ * @param error location to store any errors
+ * @returns #TRUE if the name exists, #FALSE if not or on error
+ */
+dbus_bool_t
+dbus_bus_name_has_owner (DBusConnection *connection,
+ const char *name,
+ DBusError *error)
+{
+ DBusMessage *message, *reply;
+ dbus_bool_t exists;
+
+ _dbus_return_val_if_fail (connection != NULL, FALSE);
+ _dbus_return_val_if_fail (name != NULL, FALSE);
+ _dbus_return_val_if_fail (_dbus_check_is_valid_bus_name (name), FALSE);
+ _dbus_return_val_if_error_is_set (error, FALSE);
+
+ message = dbus_message_new_method_call (DBUS_SERVICE_DBUS,
+ DBUS_PATH_DBUS,
+ DBUS_INTERFACE_DBUS,
+ "NameHasOwner");
+ if (message == NULL)
+ {
+ _DBUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ if (!dbus_message_append_args (message,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_INVALID))
+ {
+ dbus_message_unref (message);
+ _DBUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ reply = dbus_connection_send_with_reply_and_block (connection, message, -1, error);
+ dbus_message_unref (message);
+
+ if (reply == NULL)
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ return FALSE;
+ }
+
+ if (!dbus_message_get_args (reply, error,
+ DBUS_TYPE_BOOLEAN, &exists,
+ DBUS_TYPE_INVALID))
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ dbus_message_unref (reply);
+ return FALSE;
+ }
+
+ dbus_message_unref (reply);
+ return exists;
+}
+
+/**
+ * Starts a service that will request ownership of the given name.
+ * The returned result will be one of be one of
+ * #DBUS_START_REPLY_SUCCESS or #DBUS_START_REPLY_ALREADY_RUNNING if
+ * successful. Pass #NULL if you don't care about the result.
+ *
+ * The flags parameter is for future expansion, currently you should
+ * specify 0.
+ *
+ * It's often easier to avoid explicitly starting services, and
+ * just send a method call to the service's bus name instead.
+ * Method calls start a service to handle them by default
+ * unless you call dbus_message_set_auto_start() to disable this
+ * behavior.
+ *
+ * @param connection the connection
+ * @param name the name we want the new service to request
+ * @param flags the flags (should always be 0 for now)
+ * @param result a place to store the result or #NULL
+ * @param error location to store any errors
+ * @returns #TRUE if the activation succeeded, #FALSE if not
+ */
+dbus_bool_t
+dbus_bus_start_service_by_name (DBusConnection *connection,
+ const char *name,
+ dbus_uint32_t flags,
+ dbus_uint32_t *result,
+ DBusError *error)
+{
+ DBusMessage *msg;
+ DBusMessage *reply;
+
+ _dbus_return_val_if_fail (connection != NULL, FALSE);
+ _dbus_return_val_if_fail (_dbus_check_is_valid_bus_name (name), FALSE);
+
+ msg = dbus_message_new_method_call (DBUS_SERVICE_DBUS,
+ DBUS_PATH_DBUS,
+ DBUS_INTERFACE_DBUS,
+ "StartServiceByName");
+
+ if (!dbus_message_append_args (msg, DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_UINT32, &flags, DBUS_TYPE_INVALID))
+ {
+ dbus_message_unref (msg);
+ _DBUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ reply = dbus_connection_send_with_reply_and_block (connection, msg,
+ -1, error);
+ dbus_message_unref (msg);
+
+ if (reply == NULL)
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ return FALSE;
+ }
+
+ if (dbus_set_error_from_message (error, reply))
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ dbus_message_unref (reply);
+ return FALSE;
+ }
+
+ if (result != NULL &&
+ !dbus_message_get_args (reply, error, DBUS_TYPE_UINT32,
+ result, DBUS_TYPE_INVALID))
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ dbus_message_unref (reply);
+ return FALSE;
+ }
+
+ dbus_message_unref (reply);
+ return TRUE;
+}
+
+static void
+send_no_return_values (DBusConnection *connection,
+ DBusMessage *msg,
+ DBusError *error)
+{
+ if (error)
+ {
+ /* Block to check success codepath */
+ DBusMessage *reply;
+
+ reply = dbus_connection_send_with_reply_and_block (connection, msg,
+ -1, error);
+
+ if (reply == NULL)
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ else
+ dbus_message_unref (reply);
+ }
+ else
+ {
+ /* Silently-fail nonblocking codepath */
+ dbus_message_set_no_reply (msg, TRUE);
+ dbus_connection_send (connection, msg, NULL);
+ }
+}
+
+/**
+ * Adds a match rule to match messages going through the message bus.
+ * The "rule" argument is the string form of a match rule.
+ *
+ * If you pass #NULL for the error, this function will not
+ * block; the match thus won't be added until you flush the
+ * connection, and if there's an error adding the match
+ * you won't find out about it. This is generally acceptable, since the
+ * possible errors (including a lack of resources in the bus, the connection
+ * having exceeded its quota of active match rules, or the match rule being
+ * unparseable) are generally unrecoverable.
+ *
+ * If you pass non-#NULL for the error this function will
+ * block until it gets a reply. This may be useful when using match rule keys
+ * introduced in recent versions of D-Bus, like 'arg0namespace', to allow the
+ * application to fall back to less efficient match rules supported by older
+ * versions of the daemon if the running version is not new enough; or when
+ * using user-supplied rules rather than rules hard-coded at compile time.
+ *
+ * Normal API conventions would have the function return
+ * a boolean value indicating whether the error was set,
+ * but that would require blocking always to determine
+ * the return value.
+ *
+ * The AddMatch method is fully documented in the D-Bus
+ * specification. For quick reference, the format of the
+ * match rules is discussed here, but the specification
+ * is the canonical version of this information.
+ *
+ * Rules are specified as a string of comma separated
+ * key/value pairs. An example is
+ * "type='signal',sender='org.freedesktop.DBus',
+ * interface='org.freedesktop.DBus',member='Foo',
+ * path='/bar/foo',destination=':452345.34'"
+ *
+ * Possible keys you can match on are type, sender,
+ * interface, member, path, destination and numbered
+ * keys to match message args (keys are 'arg0', 'arg1', etc.).
+ * Omitting a key from the rule indicates
+ * a wildcard match. For instance omitting
+ * the member from a match rule but adding a sender would
+ * let all messages from that sender through regardless of
+ * the member.
+ *
+ * Matches are inclusive not exclusive so as long as one
+ * rule matches the message will get through. It is important
+ * to note this because every time a message is received the
+ * application will be paged into memory to process it. This
+ * can cause performance problems such as draining batteries
+ * on embedded platforms.
+ *
+ * If you match message args ('arg0', 'arg1', and so forth)
+ * only string arguments will match. That is, arg0='5' means
+ * match the string "5" not the integer 5.
+ *
+ * Currently there is no way to match against non-string arguments.
+ *
+ * A specialised form of wildcard matching on arguments is
+ * supported for path-like namespaces. If your argument match has
+ * a 'path' suffix (eg: "arg0path='/some/path/'") then it is
+ * considered a match if the argument exactly matches the given
+ * string or if one of them ends in a '/' and is a prefix of the
+ * other.
+ *
+ * Matching on interface is tricky because method call
+ * messages only optionally specify the interface.
+ * If a message omits the interface, then it will NOT match
+ * if the rule specifies an interface name. This means match
+ * rules on method calls should not usually give an interface.
+ *
+ * However, signal messages are required to include the interface
+ * so when matching signals usually you should specify the interface
+ * in the match rule.
+ *
+ * For security reasons, you can match arguments only up to
+ * #DBUS_MAXIMUM_MATCH_RULE_ARG_NUMBER.
+ *
+ * Match rules have a maximum length of #DBUS_MAXIMUM_MATCH_RULE_LENGTH
+ * bytes.
+ *
+ * Both of these maximums are much higher than you're likely to need,
+ * they only exist because the D-Bus bus daemon has fixed limits on
+ * all resource usage.
+ *
+ * @param connection connection to the message bus
+ * @param rule textual form of match rule
+ * @param error location to store any errors
+ */
+void
+dbus_bus_add_match (DBusConnection *connection,
+ const char *rule,
+ DBusError *error)
+{
+ DBusMessage *msg;
+
+ _dbus_return_if_fail (rule != NULL);
+
+ msg = dbus_message_new_method_call (DBUS_SERVICE_DBUS,
+ DBUS_PATH_DBUS,
+ DBUS_INTERFACE_DBUS,
+ "AddMatch");
+
+ if (msg == NULL)
+ {
+ _DBUS_SET_OOM (error);
+ return;
+ }
+
+ if (!dbus_message_append_args (msg, DBUS_TYPE_STRING, &rule,
+ DBUS_TYPE_INVALID))
+ {
+ dbus_message_unref (msg);
+ _DBUS_SET_OOM (error);
+ return;
+ }
+
+ send_no_return_values (connection, msg, error);
+
+ dbus_message_unref (msg);
+}
+
+/**
+ * Removes a previously-added match rule "by value" (the most
+ * recently-added identical rule gets removed). The "rule" argument
+ * is the string form of a match rule.
+ *
+ * The bus compares match rules semantically, not textually, so
+ * whitespace and ordering don't have to be identical to
+ * the rule you passed to dbus_bus_add_match().
+ *
+ * If you pass #NULL for the error, this function will not
+ * block; otherwise it will. See detailed explanation in
+ * docs for dbus_bus_add_match().
+ *
+ * @param connection connection to the message bus
+ * @param rule textual form of match rule
+ * @param error location to store any errors
+ */
+void
+dbus_bus_remove_match (DBusConnection *connection,
+ const char *rule,
+ DBusError *error)
+{
+ DBusMessage *msg;
+
+ _dbus_return_if_fail (rule != NULL);
+
+ msg = dbus_message_new_method_call (DBUS_SERVICE_DBUS,
+ DBUS_PATH_DBUS,
+ DBUS_INTERFACE_DBUS,
+ "RemoveMatch");
+
+ if (!dbus_message_append_args (msg, DBUS_TYPE_STRING, &rule,
+ DBUS_TYPE_INVALID))
+ {
+ dbus_message_unref (msg);
+ _DBUS_SET_OOM (error);
+ return;
+ }
+
+ send_no_return_values (connection, msg, error);
+
+ dbus_message_unref (msg);
+}
+
+/** @} */
diff --git a/src/3rdparty/libdbus/dbus/dbus-bus.h b/src/3rdparty/libdbus/dbus/dbus-bus.h
new file mode 100644
index 00000000..31ade9a6
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-bus.h
@@ -0,0 +1,97 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-bus.h Convenience functions for communicating with the bus.
+ *
+ * Copyright (C) 2003 CodeFactory AB
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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
+ *
+ */
+#if !defined (DBUS_INSIDE_DBUS_H) && !defined (DBUS_COMPILATION)
+#error "Only <dbus/dbus.h> can be included directly, this file may disappear or change contents."
+#endif
+
+#ifndef DBUS_BUS_H
+#define DBUS_BUS_H
+
+#include <dbus/dbus-connection.h>
+
+DBUS_BEGIN_DECLS
+
+/**
+ * @addtogroup DBusBus
+ * @{
+ */
+
+DBUS_EXPORT
+DBusConnection *dbus_bus_get (DBusBusType type,
+ DBusError *error);
+DBUS_EXPORT
+DBusConnection *dbus_bus_get_private (DBusBusType type,
+ DBusError *error);
+
+DBUS_EXPORT
+dbus_bool_t dbus_bus_register (DBusConnection *connection,
+ DBusError *error);
+DBUS_EXPORT
+dbus_bool_t dbus_bus_set_unique_name (DBusConnection *connection,
+ const char *unique_name);
+DBUS_EXPORT
+const char* dbus_bus_get_unique_name (DBusConnection *connection);
+DBUS_EXPORT
+unsigned long dbus_bus_get_unix_user (DBusConnection *connection,
+ const char *name,
+ DBusError *error);
+DBUS_EXPORT
+char* dbus_bus_get_id (DBusConnection *connection,
+ DBusError *error);
+DBUS_EXPORT
+int dbus_bus_request_name (DBusConnection *connection,
+ const char *name,
+ unsigned int flags,
+ DBusError *error);
+DBUS_EXPORT
+int dbus_bus_release_name (DBusConnection *connection,
+ const char *name,
+ DBusError *error);
+DBUS_EXPORT
+dbus_bool_t dbus_bus_name_has_owner (DBusConnection *connection,
+ const char *name,
+ DBusError *error);
+
+DBUS_EXPORT
+dbus_bool_t dbus_bus_start_service_by_name (DBusConnection *connection,
+ const char *name,
+ dbus_uint32_t flags,
+ dbus_uint32_t *reply,
+ DBusError *error);
+
+DBUS_EXPORT
+void dbus_bus_add_match (DBusConnection *connection,
+ const char *rule,
+ DBusError *error);
+DBUS_EXPORT
+void dbus_bus_remove_match (DBusConnection *connection,
+ const char *rule,
+ DBusError *error);
+
+/** @} */
+
+DBUS_END_DECLS
+
+#endif /* DBUS_BUS_H */
diff --git a/src/3rdparty/libdbus/dbus/dbus-connection-internal.h b/src/3rdparty/libdbus/dbus/dbus-connection-internal.h
new file mode 100644
index 00000000..747e6e54
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-connection-internal.h
@@ -0,0 +1,161 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-connection-internal.h DBusConnection internal interfaces
+ *
+ * Copyright (C) 2002 Red Hat Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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
+ *
+ */
+#ifndef DBUS_CONNECTION_INTERNAL_H
+#define DBUS_CONNECTION_INTERNAL_H
+
+#include <dbus/dbus-internals.h>
+#include <dbus/dbus-connection.h>
+#include <dbus/dbus-credentials.h>
+#include <dbus/dbus-message.h>
+#include <dbus/dbus-transport.h>
+#include <dbus/dbus-resources.h>
+#include <dbus/dbus-list.h>
+#include <dbus/dbus-timeout.h>
+#include <dbus/dbus-dataslot.h>
+
+DBUS_BEGIN_DECLS
+
+typedef enum
+{
+ DBUS_ITERATION_DO_WRITING = 1 << 0, /**< Write messages out. */
+ DBUS_ITERATION_DO_READING = 1 << 1, /**< Read messages in. */
+ DBUS_ITERATION_BLOCK = 1 << 2 /**< Block if nothing to do. */
+} DBusIterationFlags;
+
+/** default timeout value when waiting for a message reply, 25 seconds */
+#define _DBUS_DEFAULT_TIMEOUT_VALUE (25 * 1000)
+
+typedef void (* DBusPendingFdsChangeFunction) (void *data);
+
+DBUS_PRIVATE_EXPORT
+void _dbus_connection_lock (DBusConnection *connection);
+DBUS_PRIVATE_EXPORT
+void _dbus_connection_unlock (DBusConnection *connection);
+DBUS_PRIVATE_EXPORT
+DBusConnection * _dbus_connection_ref_unlocked (DBusConnection *connection);
+DBUS_PRIVATE_EXPORT
+void _dbus_connection_unref_unlocked (DBusConnection *connection);
+DBUS_PRIVATE_EXPORT
+dbus_uint32_t _dbus_connection_get_next_client_serial (DBusConnection *connection);
+void _dbus_connection_queue_received_message_link (DBusConnection *connection,
+ DBusList *link);
+dbus_bool_t _dbus_connection_has_messages_to_send_unlocked (DBusConnection *connection);
+DBusMessage* _dbus_connection_get_message_to_send (DBusConnection *connection);
+void _dbus_connection_message_sent_unlocked (DBusConnection *connection,
+ DBusMessage *message);
+dbus_bool_t _dbus_connection_add_watch_unlocked (DBusConnection *connection,
+ DBusWatch *watch);
+void _dbus_connection_remove_watch_unlocked (DBusConnection *connection,
+ DBusWatch *watch);
+void _dbus_connection_toggle_watch_unlocked (DBusConnection *connection,
+ DBusWatch *watch,
+ dbus_bool_t enabled);
+dbus_bool_t _dbus_connection_handle_watch (DBusWatch *watch,
+ unsigned int condition,
+ void *data);
+dbus_bool_t _dbus_connection_add_timeout_unlocked (DBusConnection *connection,
+ DBusTimeout *timeout);
+void _dbus_connection_remove_timeout_unlocked (DBusConnection *connection,
+ DBusTimeout *timeout);
+void _dbus_connection_toggle_timeout_unlocked (DBusConnection *connection,
+ DBusTimeout *timeout,
+ dbus_bool_t enabled);
+DBusConnection* _dbus_connection_new_for_transport (DBusTransport *transport);
+void _dbus_connection_do_iteration_unlocked (DBusConnection *connection,
+ DBusPendingCall *pending,
+ unsigned int flags,
+ int timeout_milliseconds);
+void _dbus_connection_close_possibly_shared (DBusConnection *connection);
+void _dbus_connection_close_if_only_one_ref (DBusConnection *connection);
+
+DBusPendingCall* _dbus_pending_call_new (DBusConnection *connection,
+ int timeout_milliseconds,
+ DBusTimeoutHandler timeout_handler);
+void _dbus_pending_call_notify (DBusPendingCall *pending);
+void _dbus_connection_remove_pending_call (DBusConnection *connection,
+ DBusPendingCall *pending);
+void _dbus_connection_block_pending_call (DBusPendingCall *pending);
+void _dbus_pending_call_complete_and_unlock (DBusPendingCall *pending,
+ DBusMessage *message);
+dbus_bool_t _dbus_connection_send_and_unlock (DBusConnection *connection,
+ DBusMessage *message,
+ dbus_uint32_t *client_serial);
+
+void _dbus_connection_queue_synthesized_message_link (DBusConnection *connection,
+ DBusList *link);
+DBUS_PRIVATE_EXPORT
+void _dbus_connection_test_get_locks (DBusConnection *conn,
+ DBusMutex **mutex_loc,
+ DBusMutex **dispatch_mutex_loc,
+ DBusMutex **io_path_mutex_loc,
+ DBusCondVar **dispatch_cond_loc,
+ DBusCondVar **io_path_cond_loc);
+DBUS_PRIVATE_EXPORT
+int _dbus_connection_get_pending_fds_count (DBusConnection *connection);
+DBUS_PRIVATE_EXPORT
+void _dbus_connection_set_pending_fds_function (DBusConnection *connection,
+ DBusPendingFdsChangeFunction callback,
+ void *data);
+
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_connection_get_linux_security_label (DBusConnection *connection,
+ char **label_p);
+DBUS_PRIVATE_EXPORT
+DBusCredentials *_dbus_connection_get_credentials (DBusConnection *connection);
+
+/* if DBUS_ENABLE_STATS */
+DBUS_PRIVATE_EXPORT
+void _dbus_connection_get_stats (DBusConnection *connection,
+ dbus_uint32_t *in_messages,
+ dbus_uint32_t *in_bytes,
+ dbus_uint32_t *in_fds,
+ dbus_uint32_t *in_peak_bytes,
+ dbus_uint32_t *in_peak_fds,
+ dbus_uint32_t *out_messages,
+ dbus_uint32_t *out_bytes,
+ dbus_uint32_t *out_fds,
+ dbus_uint32_t *out_peak_bytes,
+ dbus_uint32_t *out_peak_fds);
+
+
+DBUS_EMBEDDED_TESTS_EXPORT
+const char* _dbus_connection_get_address (DBusConnection *connection);
+
+/* This _dbus_bus_* stuff doesn't really belong here, but dbus-bus-internal.h seems
+ * silly for one function
+ */
+/**
+ * @addtogroup DBusBusInternals
+ * @{
+ */
+
+void _dbus_bus_notify_shared_connection_disconnected_unlocked (DBusConnection *connection);
+
+/** @} */
+
+
+DBUS_END_DECLS
+
+#endif /* DBUS_CONNECTION_INTERNAL_H */
diff --git a/src/3rdparty/libdbus/dbus/dbus-connection.c b/src/3rdparty/libdbus/dbus/dbus-connection.c
new file mode 100644
index 00000000..13f5085f
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-connection.c
@@ -0,0 +1,6451 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-connection.c DBusConnection object
+ *
+ * Copyright (C) 2002-2006 Red Hat Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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-shared.h"
+#include "dbus-connection.h"
+#include "dbus-list.h"
+#include "dbus-timeout.h"
+#include "dbus-transport.h"
+#include "dbus-watch.h"
+#include "dbus-connection-internal.h"
+#include "dbus-pending-call-internal.h"
+#include "dbus-list.h"
+#include "dbus-hash.h"
+#include "dbus-message-internal.h"
+#include "dbus-message-private.h"
+#include "dbus-threads.h"
+#include "dbus-protocol.h"
+#include "dbus-dataslot.h"
+#include "dbus-string.h"
+#include "dbus-signature.h"
+#include "dbus-pending-call.h"
+#include "dbus-object-tree.h"
+#include "dbus-threads-internal.h"
+#include "dbus-bus.h"
+#include "dbus-marshal-basic.h"
+
+#ifdef DBUS_DISABLE_CHECKS
+#define TOOK_LOCK_CHECK(connection)
+#define RELEASING_LOCK_CHECK(connection)
+#define HAVE_LOCK_CHECK(connection)
+#else
+#define TOOK_LOCK_CHECK(connection) do { \
+ _dbus_assert (!(connection)->have_connection_lock); \
+ (connection)->have_connection_lock = TRUE; \
+ } while (0)
+#define RELEASING_LOCK_CHECK(connection) do { \
+ _dbus_assert ((connection)->have_connection_lock); \
+ (connection)->have_connection_lock = FALSE; \
+ } while (0)
+#define HAVE_LOCK_CHECK(connection) _dbus_assert ((connection)->have_connection_lock)
+/* A "DO_NOT_HAVE_LOCK_CHECK" is impossible since we need the lock to check the flag */
+#endif
+
+#define TRACE_LOCKS 1
+
+#define CONNECTION_LOCK(connection) do { \
+ if (TRACE_LOCKS) { _dbus_verbose ("LOCK\n"); } \
+ _dbus_rmutex_lock ((connection)->mutex); \
+ TOOK_LOCK_CHECK (connection); \
+ } while (0)
+
+#define CONNECTION_UNLOCK(connection) _dbus_connection_unlock (connection)
+
+#define SLOTS_LOCK(connection) do { \
+ _dbus_rmutex_lock ((connection)->slot_mutex); \
+ } while (0)
+
+#define SLOTS_UNLOCK(connection) do { \
+ _dbus_rmutex_unlock ((connection)->slot_mutex); \
+ } while (0)
+
+#define DISPATCH_STATUS_NAME(s) \
+ ((s) == DBUS_DISPATCH_COMPLETE ? "complete" : \
+ (s) == DBUS_DISPATCH_DATA_REMAINS ? "data remains" : \
+ (s) == DBUS_DISPATCH_NEED_MEMORY ? "need memory" : \
+ "???")
+
+/**
+ * @defgroup DBusConnection DBusConnection
+ * @ingroup DBus
+ * @brief Connection to another application
+ *
+ * A DBusConnection represents a connection to another
+ * application. Messages can be sent and received via this connection.
+ * The other application may be a message bus; for convenience, the
+ * function dbus_bus_get() is provided to automatically open a
+ * connection to the well-known message buses.
+ *
+ * In brief a DBusConnection is a message queue associated with some
+ * message transport mechanism such as a socket. The connection
+ * maintains a queue of incoming messages and a queue of outgoing
+ * messages.
+ *
+ * Several functions use the following terms:
+ * <ul>
+ * <li><b>read</b> means to fill the incoming message queue by reading from the socket</li>
+ * <li><b>write</b> means to drain the outgoing queue by writing to the socket</li>
+ * <li><b>dispatch</b> means to drain the incoming queue by invoking application-provided message handlers</li>
+ * </ul>
+ *
+ * The function dbus_connection_read_write_dispatch() for example does all
+ * three of these things, offering a simple alternative to a main loop.
+ *
+ * In an application with a main loop, the read/write/dispatch
+ * operations are usually separate.
+ *
+ * The connection provides #DBusWatch and #DBusTimeout objects to
+ * the main loop. These are used to know when reading, writing, or
+ * dispatching should be performed.
+ *
+ * Incoming messages are processed
+ * by calling dbus_connection_dispatch(). dbus_connection_dispatch()
+ * runs any handlers registered for the topmost message in the message
+ * queue, then discards the message, then returns.
+ *
+ * dbus_connection_get_dispatch_status() indicates whether
+ * messages are currently in the queue that need dispatching.
+ * dbus_connection_set_dispatch_status_function() allows
+ * you to set a function to be used to monitor the dispatch status.
+ *
+ * If you're using GLib or Qt add-on libraries for D-Bus, there are
+ * special convenience APIs in those libraries that hide
+ * all the details of dispatch and watch/timeout monitoring.
+ * For example, dbus_connection_setup_with_g_main().
+ *
+ * If you aren't using these add-on libraries, but want to process
+ * messages asynchronously, you must manually call
+ * dbus_connection_set_dispatch_status_function(),
+ * dbus_connection_set_watch_functions(),
+ * dbus_connection_set_timeout_functions() providing appropriate
+ * functions to integrate the connection with your application's main
+ * loop. This can be tricky to get right; main loops are not simple.
+ *
+ * If you don't need to be asynchronous, you can ignore #DBusWatch,
+ * #DBusTimeout, and dbus_connection_dispatch(). Instead,
+ * dbus_connection_read_write_dispatch() can be used.
+ *
+ * Or, in <em>very</em> simple applications,
+ * dbus_connection_pop_message() may be all you need, allowing you to
+ * avoid setting up any handler functions (see
+ * dbus_connection_add_filter(),
+ * dbus_connection_register_object_path() for more on handlers).
+ *
+ * When you use dbus_connection_send() or one of its variants to send
+ * a message, the message is added to the outgoing queue. It's
+ * actually written to the network later; either in
+ * dbus_watch_handle() invoked by your main loop, or in
+ * dbus_connection_flush() which blocks until it can write out the
+ * entire outgoing queue. The GLib/Qt add-on libraries again
+ * handle the details here for you by setting up watch functions.
+ *
+ * When a connection is disconnected, you are guaranteed to get a
+ * signal "Disconnected" from the interface
+ * #DBUS_INTERFACE_LOCAL, path
+ * #DBUS_PATH_LOCAL.
+ *
+ * You may not drop the last reference to a #DBusConnection
+ * until that connection has been disconnected.
+ *
+ * You may dispatch the unprocessed incoming message queue even if the
+ * connection is disconnected. However, "Disconnected" will always be
+ * the last message in the queue (obviously no messages are received
+ * after disconnection).
+ *
+ * After calling dbus_threads_init(), #DBusConnection has thread
+ * locks and drops them when invoking user callbacks, so in general is
+ * transparently threadsafe. However, #DBusMessage does NOT have
+ * thread locks; you must not send the same message to multiple
+ * #DBusConnection if those connections will be used from different threads,
+ * for example.
+ *
+ * Also, if you dispatch or pop messages from multiple threads, it
+ * may work in the sense that it won't crash, but it's tough to imagine
+ * sane results; it will be completely unpredictable which messages
+ * go to which threads.
+ *
+ * It's recommended to dispatch from a single thread.
+ *
+ * The most useful function to call from multiple threads at once
+ * is dbus_connection_send_with_reply_and_block(). That is,
+ * multiple threads can make method calls at the same time.
+ *
+ * If you aren't using threads, you can use a main loop and
+ * dbus_pending_call_set_notify() to achieve a similar result.
+ */
+
+/**
+ * @defgroup DBusConnectionInternals DBusConnection implementation details
+ * @ingroup DBusInternals
+ * @brief Implementation details of DBusConnection
+ *
+ * @{
+ */
+
+static void
+_dbus_connection_trace_ref (DBusConnection *connection,
+ int old_refcount,
+ int new_refcount,
+ const char *why)
+{
+#ifdef DBUS_ENABLE_VERBOSE_MODE
+ static int enabled = -1;
+
+ _dbus_trace_ref ("DBusConnection", connection, old_refcount, new_refcount,
+ why, "DBUS_CONNECTION_TRACE", &enabled);
+#endif
+}
+
+/**
+ * Internal struct representing a message filter function
+ */
+typedef struct DBusMessageFilter DBusMessageFilter;
+
+/**
+ * Internal struct representing a message filter function
+ */
+struct DBusMessageFilter
+{
+ DBusAtomic refcount; /**< Reference count */
+ DBusHandleMessageFunction function; /**< Function to call to filter */
+ void *user_data; /**< User data for the function */
+ DBusFreeFunction free_user_data_function; /**< Function to free the user data */
+};
+
+
+/**
+ * Internals of DBusPreallocatedSend
+ */
+struct DBusPreallocatedSend
+{
+ DBusConnection *connection; /**< Connection we'd send the message to */
+ DBusList *queue_link; /**< Preallocated link in the queue */
+ DBusList *counter_link; /**< Preallocated link in the resource counter */
+};
+
+#if HAVE_DECL_MSG_NOSIGNAL
+static DBusAtomic _dbus_modify_sigpipe = { FALSE };
+#else
+static DBusAtomic _dbus_modify_sigpipe = { TRUE };
+#endif
+
+/**
+ * Implementation details of DBusConnection. All fields are private.
+ */
+struct DBusConnection
+{
+ DBusAtomic refcount; /**< Reference count. */
+
+ DBusRMutex *mutex; /**< Lock on the entire DBusConnection */
+
+ DBusCMutex *dispatch_mutex; /**< Protects dispatch_acquired */
+ DBusCondVar *dispatch_cond; /**< Notify when dispatch_acquired is available */
+ DBusCMutex *io_path_mutex; /**< Protects io_path_acquired */
+ DBusCondVar *io_path_cond; /**< Notify when io_path_acquired is available */
+
+ DBusList *outgoing_messages; /**< Queue of messages we need to send, send the end of the list first. */
+ DBusList *incoming_messages; /**< Queue of messages we have received, end of the list received most recently. */
+ DBusList *expired_messages; /**< Messages that will be released when we next unlock. */
+
+ DBusMessage *message_borrowed; /**< Filled in if the first incoming message has been borrowed;
+ * dispatch_acquired will be set by the borrower
+ */
+
+ int n_outgoing; /**< Length of outgoing queue. */
+ int n_incoming; /**< Length of incoming queue. */
+
+ DBusCounter *outgoing_counter; /**< Counts size of outgoing messages. */
+
+ DBusTransport *transport; /**< Object that sends/receives messages over network. */
+ DBusWatchList *watches; /**< Stores active watches. */
+ DBusTimeoutList *timeouts; /**< Stores active timeouts. */
+
+ DBusList *filter_list; /**< List of filters. */
+
+ DBusRMutex *slot_mutex; /**< Lock on slot_list so overall connection lock need not be taken */
+ DBusDataSlotList slot_list; /**< Data stored by allocated integer ID */
+
+ DBusHashTable *pending_replies; /**< Hash of message serials to #DBusPendingCall. */
+
+ dbus_uint32_t client_serial; /**< Client serial. Increments each time a message is sent */
+ DBusList *disconnect_message_link; /**< Preallocated list node for queueing the disconnection message */
+
+ DBusWakeupMainFunction wakeup_main_function; /**< Function to wake up the mainloop */
+ void *wakeup_main_data; /**< Application data for wakeup_main_function */
+ DBusFreeFunction free_wakeup_main_data; /**< free wakeup_main_data */
+
+ DBusDispatchStatusFunction dispatch_status_function; /**< Function on dispatch status changes */
+ void *dispatch_status_data; /**< Application data for dispatch_status_function */
+ DBusFreeFunction free_dispatch_status_data; /**< free dispatch_status_data */
+
+ DBusDispatchStatus last_dispatch_status; /**< The last dispatch status we reported to the application. */
+
+ DBusObjectTree *objects; /**< Object path handlers registered with this connection */
+
+ char *server_guid; /**< GUID of server if we are in shared_connections, #NULL if server GUID is unknown or connection is private */
+
+ /* These two MUST be bools and not bitfields, because they are protected by a separate lock
+ * from connection->mutex and all bitfields in a word have to be read/written together.
+ * So you can't have a different lock for different bitfields in the same word.
+ */
+ dbus_bool_t dispatch_acquired; /**< Someone has dispatch path (can drain incoming queue) */
+ dbus_bool_t io_path_acquired; /**< Someone has transport io path (can use the transport to read/write messages) */
+
+ unsigned int shareable : 1; /**< #TRUE if libdbus owns a reference to the connection and can return it from dbus_connection_open() more than once */
+
+ unsigned int exit_on_disconnect : 1; /**< If #TRUE, exit after handling disconnect signal */
+
+ unsigned int builtin_filters_enabled : 1; /**< If #TRUE, handle org.freedesktop.DBus.Peer messages automatically, whether they have a bus name or not */
+
+ unsigned int route_peer_messages : 1; /**< If #TRUE, if org.freedesktop.DBus.Peer messages have a bus name, don't handle them automatically */
+
+ unsigned int disconnected_message_arrived : 1; /**< We popped or are dispatching the disconnected message.
+ * if the disconnect_message_link is NULL then we queued it, but
+ * this flag is whether it got to the head of the queue.
+ */
+ unsigned int disconnected_message_processed : 1; /**< We did our default handling of the disconnected message,
+ * such as closing the connection.
+ */
+
+#ifndef DBUS_DISABLE_CHECKS
+ unsigned int have_connection_lock : 1; /**< Used to check locking */
+#endif
+
+#if defined(DBUS_ENABLE_CHECKS) || defined(DBUS_ENABLE_ASSERT)
+ int generation; /**< _dbus_current_generation that should correspond to this connection */
+#endif
+};
+
+static DBusDispatchStatus _dbus_connection_get_dispatch_status_unlocked (DBusConnection *connection);
+static void _dbus_connection_update_dispatch_status_and_unlock (DBusConnection *connection,
+ DBusDispatchStatus new_status);
+static void _dbus_connection_last_unref (DBusConnection *connection);
+static void _dbus_connection_acquire_dispatch (DBusConnection *connection);
+static void _dbus_connection_release_dispatch (DBusConnection *connection);
+static DBusDispatchStatus _dbus_connection_flush_unlocked (DBusConnection *connection);
+static void _dbus_connection_close_possibly_shared_and_unlock (DBusConnection *connection);
+static dbus_bool_t _dbus_connection_get_is_connected_unlocked (DBusConnection *connection);
+static dbus_bool_t _dbus_connection_peek_for_reply_unlocked (DBusConnection *connection,
+ dbus_uint32_t client_serial);
+
+static DBusMessageFilter *
+_dbus_message_filter_ref (DBusMessageFilter *filter)
+{
+#ifdef DBUS_DISABLE_ASSERT
+ _dbus_atomic_inc (&filter->refcount);
+#else
+ dbus_int32_t old_value;
+
+ old_value = _dbus_atomic_inc (&filter->refcount);
+ _dbus_assert (old_value > 0);
+#endif
+
+ return filter;
+}
+
+static void
+_dbus_message_filter_unref (DBusMessageFilter *filter)
+{
+ dbus_int32_t old_value;
+
+ old_value = _dbus_atomic_dec (&filter->refcount);
+ _dbus_assert (old_value > 0);
+
+ if (old_value == 1)
+ {
+ if (filter->free_user_data_function)
+ (* filter->free_user_data_function) (filter->user_data);
+
+ dbus_free (filter);
+ }
+}
+
+/**
+ * Acquires the connection lock.
+ *
+ * @param connection the connection.
+ */
+void
+_dbus_connection_lock (DBusConnection *connection)
+{
+ CONNECTION_LOCK (connection);
+}
+
+/**
+ * Releases the connection lock.
+ *
+ * @param connection the connection.
+ */
+void
+_dbus_connection_unlock (DBusConnection *connection)
+{
+ DBusList *expired_messages;
+ DBusList *iter;
+
+ if (TRACE_LOCKS)
+ {
+ _dbus_verbose ("UNLOCK\n");
+ }
+
+ /* If we had messages that expired (fell off the incoming or outgoing
+ * queues) while we were locked, actually release them now */
+ expired_messages = connection->expired_messages;
+ connection->expired_messages = NULL;
+
+ RELEASING_LOCK_CHECK (connection);
+ _dbus_rmutex_unlock (connection->mutex);
+
+ for (iter = _dbus_list_pop_first_link (&expired_messages);
+ iter != NULL;
+ iter = _dbus_list_pop_first_link (&expired_messages))
+ {
+ DBusMessage *message = iter->data;
+
+ dbus_message_unref (message);
+ _dbus_list_free_link (iter);
+ }
+}
+
+/**
+ * Wakes up the main loop if it is sleeping
+ * Needed if we're e.g. queueing outgoing messages
+ * on a thread while the mainloop sleeps.
+ *
+ * @param connection the connection.
+ */
+static void
+_dbus_connection_wakeup_mainloop (DBusConnection *connection)
+{
+ if (connection->wakeup_main_function)
+ (*connection->wakeup_main_function) (connection->wakeup_main_data);
+}
+
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
+/**
+ * Gets the locks so we can examine them
+ *
+ * @param connection the connection.
+ * @param mutex_loc return for the location of the main mutex pointer
+ * @param dispatch_mutex_loc return location of the dispatch mutex pointer
+ * @param io_path_mutex_loc return location of the io_path mutex pointer
+ * @param dispatch_cond_loc return location of the dispatch conditional
+ * variable pointer
+ * @param io_path_cond_loc return location of the io_path conditional
+ * variable pointer
+ */
+void
+_dbus_connection_test_get_locks (DBusConnection *connection,
+ DBusMutex **mutex_loc,
+ DBusMutex **dispatch_mutex_loc,
+ DBusMutex **io_path_mutex_loc,
+ DBusCondVar **dispatch_cond_loc,
+ DBusCondVar **io_path_cond_loc)
+{
+ *mutex_loc = (DBusMutex *) connection->mutex;
+ *dispatch_mutex_loc = (DBusMutex *) connection->dispatch_mutex;
+ *io_path_mutex_loc = (DBusMutex *) connection->io_path_mutex;
+ *dispatch_cond_loc = connection->dispatch_cond;
+ *io_path_cond_loc = connection->io_path_cond;
+}
+#endif
+
+/**
+ * Adds a message-containing list link to the incoming message queue,
+ * taking ownership of the link and the message's current refcount.
+ * Cannot fail due to lack of memory.
+ *
+ * @param connection the connection.
+ * @param link the message link to queue.
+ */
+void
+_dbus_connection_queue_received_message_link (DBusConnection *connection,
+ DBusList *link)
+{
+ DBusPendingCall *pending;
+ dbus_uint32_t reply_serial;
+ DBusMessage *message;
+
+ _dbus_assert (_dbus_transport_peek_is_authenticated (connection->transport));
+
+ _dbus_list_append_link (&connection->incoming_messages,
+ link);
+ message = link->data;
+
+ /* If this is a reply we're waiting on, remove timeout for it */
+ reply_serial = dbus_message_get_reply_serial (message);
+ if (reply_serial != 0)
+ {
+ pending = _dbus_hash_table_lookup_int (connection->pending_replies,
+ reply_serial);
+ if (pending != NULL)
+ {
+ if (_dbus_pending_call_is_timeout_added_unlocked (pending))
+ _dbus_connection_remove_timeout_unlocked (connection,
+ _dbus_pending_call_get_timeout_unlocked (pending));
+
+ _dbus_pending_call_set_timeout_added_unlocked (pending, FALSE);
+ }
+ }
+
+
+
+ connection->n_incoming += 1;
+
+ _dbus_connection_wakeup_mainloop (connection);
+
+ _dbus_verbose ("Message %p (%s %s %s %s '%s' reply to %u) added to incoming queue %p, %d incoming\n",
+ message,
+ dbus_message_type_to_string (dbus_message_get_type (message)),
+ dbus_message_get_path (message) ?
+ dbus_message_get_path (message) :
+ "no path",
+ dbus_message_get_interface (message) ?
+ dbus_message_get_interface (message) :
+ "no interface",
+ dbus_message_get_member (message) ?
+ dbus_message_get_member (message) :
+ "no member",
+ dbus_message_get_signature (message),
+ dbus_message_get_reply_serial (message),
+ connection,
+ connection->n_incoming);
+
+ _dbus_message_trace_ref (message, -1, -1,
+ "_dbus_conection_queue_received_message_link");
+}
+
+/**
+ * Adds a link + message to the incoming message queue.
+ * Can't fail. Takes ownership of both link and message.
+ *
+ * @param connection the connection.
+ * @param link the list node and message to queue.
+ *
+ */
+void
+_dbus_connection_queue_synthesized_message_link (DBusConnection *connection,
+ DBusList *link)
+{
+ HAVE_LOCK_CHECK (connection);
+
+ _dbus_list_append_link (&connection->incoming_messages, link);
+
+ connection->n_incoming += 1;
+
+ _dbus_connection_wakeup_mainloop (connection);
+
+ _dbus_message_trace_ref (link->data, -1, -1,
+ "_dbus_connection_queue_synthesized_message_link");
+
+ _dbus_verbose ("Synthesized message %p added to incoming queue %p, %d incoming\n",
+ link->data, connection, connection->n_incoming);
+}
+
+
+/**
+ * Checks whether there are messages in the outgoing message queue.
+ * Called with connection lock held.
+ *
+ * @param connection the connection.
+ * @returns #TRUE if the outgoing queue is non-empty.
+ */
+dbus_bool_t
+_dbus_connection_has_messages_to_send_unlocked (DBusConnection *connection)
+{
+ HAVE_LOCK_CHECK (connection);
+ return connection->outgoing_messages != NULL;
+}
+
+/**
+ * Checks whether there are messages in the outgoing message queue.
+ * Use dbus_connection_flush() to block until all outgoing
+ * messages have been written to the underlying transport
+ * (such as a socket).
+ *
+ * @param connection the connection.
+ * @returns #TRUE if the outgoing queue is non-empty.
+ */
+dbus_bool_t
+dbus_connection_has_messages_to_send (DBusConnection *connection)
+{
+ dbus_bool_t v;
+
+ _dbus_return_val_if_fail (connection != NULL, FALSE);
+
+ CONNECTION_LOCK (connection);
+ v = _dbus_connection_has_messages_to_send_unlocked (connection);
+ CONNECTION_UNLOCK (connection);
+
+ return v;
+}
+
+/**
+ * Gets the next outgoing message. The message remains in the
+ * queue, and the caller does not own a reference to it.
+ *
+ * @param connection the connection.
+ * @returns the message to be sent.
+ */
+DBusMessage*
+_dbus_connection_get_message_to_send (DBusConnection *connection)
+{
+ HAVE_LOCK_CHECK (connection);
+
+ return _dbus_list_get_last (&connection->outgoing_messages);
+}
+
+/**
+ * Notifies the connection that a message has been sent, so the
+ * message can be removed from the outgoing queue.
+ * Called with the connection lock held.
+ *
+ * @param connection the connection.
+ * @param message the message that was sent.
+ */
+void
+_dbus_connection_message_sent_unlocked (DBusConnection *connection,
+ DBusMessage *message)
+{
+ DBusList *link;
+
+ HAVE_LOCK_CHECK (connection);
+
+ /* This can be called before we even complete authentication, since
+ * it's called on disconnect to clean up the outgoing queue.
+ * It's also called as we successfully send each message.
+ */
+
+ link = _dbus_list_get_last_link (&connection->outgoing_messages);
+ _dbus_assert (link != NULL);
+ _dbus_assert (link->data == message);
+
+ _dbus_list_unlink (&connection->outgoing_messages,
+ link);
+ _dbus_list_prepend_link (&connection->expired_messages, link);
+
+ connection->n_outgoing -= 1;
+
+ _dbus_verbose ("Message %p (%s %s %s %s '%s') removed from outgoing queue %p, %d left to send\n",
+ message,
+ dbus_message_type_to_string (dbus_message_get_type (message)),
+ dbus_message_get_path (message) ?
+ dbus_message_get_path (message) :
+ "no path",
+ dbus_message_get_interface (message) ?
+ dbus_message_get_interface (message) :
+ "no interface",
+ dbus_message_get_member (message) ?
+ dbus_message_get_member (message) :
+ "no member",
+ dbus_message_get_signature (message),
+ connection, connection->n_outgoing);
+
+ /* It's OK that in principle we call the notify function, because for the
+ * outgoing limit, there isn't one */
+ _dbus_message_remove_counter (message, connection->outgoing_counter);
+
+ /* The message will actually be unreffed when we unlock */
+}
+
+/** Function to be called in protected_change_watch() with refcount held */
+typedef dbus_bool_t (* DBusWatchAddFunction) (DBusWatchList *list,
+ DBusWatch *watch);
+/** Function to be called in protected_change_watch() with refcount held */
+typedef void (* DBusWatchRemoveFunction) (DBusWatchList *list,
+ DBusWatch *watch);
+/** Function to be called in protected_change_watch() with refcount held */
+typedef void (* DBusWatchToggleFunction) (DBusWatchList *list,
+ DBusWatch *watch,
+ dbus_bool_t enabled);
+
+static dbus_bool_t
+protected_change_watch (DBusConnection *connection,
+ DBusWatch *watch,
+ DBusWatchAddFunction add_function,
+ DBusWatchRemoveFunction remove_function,
+ DBusWatchToggleFunction toggle_function,
+ dbus_bool_t enabled)
+{
+ dbus_bool_t retval;
+
+ HAVE_LOCK_CHECK (connection);
+
+ /* The original purpose of protected_change_watch() was to hold a
+ * ref on the connection while dropping the connection lock, then
+ * calling out to the app. This was a broken hack that did not
+ * work, since the connection was in a hosed state (no WatchList
+ * field) while calling out.
+ *
+ * So for now we'll just keep the lock while calling out. This means
+ * apps are not allowed to call DBusConnection methods inside a
+ * watch function or they will deadlock.
+ *
+ * The "real fix" is to use the _and_unlock() pattern found
+ * elsewhere in the code, to defer calling out to the app until
+ * we're about to drop locks and return flow of control to the app
+ * anyway.
+ *
+ * See http://lists.freedesktop.org/archives/dbus/2007-July/thread.html#8144
+ */
+
+ if (connection->watches)
+ {
+ if (add_function)
+ retval = (* add_function) (connection->watches, watch);
+ else if (remove_function)
+ {
+ retval = TRUE;
+ (* remove_function) (connection->watches, watch);
+ }
+ else
+ {
+ retval = TRUE;
+ (* toggle_function) (connection->watches, watch, enabled);
+ }
+ return retval;
+ }
+ else
+ return FALSE;
+}
+
+
+/**
+ * Adds a watch using the connection's DBusAddWatchFunction if
+ * available. Otherwise records the watch to be added when said
+ * function is available. Also re-adds the watch if the
+ * DBusAddWatchFunction changes. May fail due to lack of memory.
+ * Connection lock should be held when calling this.
+ *
+ * @param connection the connection.
+ * @param watch the watch to add.
+ * @returns #TRUE on success.
+ */
+dbus_bool_t
+_dbus_connection_add_watch_unlocked (DBusConnection *connection,
+ DBusWatch *watch)
+{
+ return protected_change_watch (connection, watch,
+ _dbus_watch_list_add_watch,
+ NULL, NULL, FALSE);
+}
+
+/**
+ * Removes a watch using the connection's DBusRemoveWatchFunction
+ * if available. It's an error to call this function on a watch
+ * that was not previously added.
+ * Connection lock should be held when calling this.
+ *
+ * @param connection the connection.
+ * @param watch the watch to remove.
+ */
+void
+_dbus_connection_remove_watch_unlocked (DBusConnection *connection,
+ DBusWatch *watch)
+{
+ protected_change_watch (connection, watch,
+ NULL,
+ _dbus_watch_list_remove_watch,
+ NULL, FALSE);
+}
+
+/**
+ * Toggles a watch and notifies app via connection's
+ * DBusWatchToggledFunction if available. It's an error to call this
+ * function on a watch that was not previously added.
+ * Connection lock should be held when calling this.
+ *
+ * @param connection the connection.
+ * @param watch the watch to toggle.
+ * @param enabled whether to enable or disable
+ */
+void
+_dbus_connection_toggle_watch_unlocked (DBusConnection *connection,
+ DBusWatch *watch,
+ dbus_bool_t enabled)
+{
+ _dbus_assert (watch != NULL);
+
+ protected_change_watch (connection, watch,
+ NULL, NULL,
+ _dbus_watch_list_toggle_watch,
+ enabled);
+}
+
+/** Function to be called in protected_change_timeout() with refcount held */
+typedef dbus_bool_t (* DBusTimeoutAddFunction) (DBusTimeoutList *list,
+ DBusTimeout *timeout);
+/** Function to be called in protected_change_timeout() with refcount held */
+typedef void (* DBusTimeoutRemoveFunction) (DBusTimeoutList *list,
+ DBusTimeout *timeout);
+/** Function to be called in protected_change_timeout() with refcount held */
+typedef void (* DBusTimeoutToggleFunction) (DBusTimeoutList *list,
+ DBusTimeout *timeout,
+ dbus_bool_t enabled);
+
+static dbus_bool_t
+protected_change_timeout (DBusConnection *connection,
+ DBusTimeout *timeout,
+ DBusTimeoutAddFunction add_function,
+ DBusTimeoutRemoveFunction remove_function,
+ DBusTimeoutToggleFunction toggle_function,
+ dbus_bool_t enabled)
+{
+ dbus_bool_t retval;
+
+ HAVE_LOCK_CHECK (connection);
+
+ /* The original purpose of protected_change_timeout() was to hold a
+ * ref on the connection while dropping the connection lock, then
+ * calling out to the app. This was a broken hack that did not
+ * work, since the connection was in a hosed state (no TimeoutList
+ * field) while calling out.
+ *
+ * So for now we'll just keep the lock while calling out. This means
+ * apps are not allowed to call DBusConnection methods inside a
+ * timeout function or they will deadlock.
+ *
+ * The "real fix" is to use the _and_unlock() pattern found
+ * elsewhere in the code, to defer calling out to the app until
+ * we're about to drop locks and return flow of control to the app
+ * anyway.
+ *
+ * See http://lists.freedesktop.org/archives/dbus/2007-July/thread.html#8144
+ */
+
+ if (connection->timeouts)
+ {
+ if (add_function)
+ retval = (* add_function) (connection->timeouts, timeout);
+ else if (remove_function)
+ {
+ retval = TRUE;
+ (* remove_function) (connection->timeouts, timeout);
+ }
+ else
+ {
+ retval = TRUE;
+ (* toggle_function) (connection->timeouts, timeout, enabled);
+ }
+ return retval;
+ }
+ else
+ return FALSE;
+}
+
+/**
+ * Adds a timeout using the connection's DBusAddTimeoutFunction if
+ * available. Otherwise records the timeout to be added when said
+ * function is available. Also re-adds the timeout if the
+ * DBusAddTimeoutFunction changes. May fail due to lack of memory.
+ * The timeout will fire repeatedly until removed.
+ * Connection lock should be held when calling this.
+ *
+ * @param connection the connection.
+ * @param timeout the timeout to add.
+ * @returns #TRUE on success.
+ */
+dbus_bool_t
+_dbus_connection_add_timeout_unlocked (DBusConnection *connection,
+ DBusTimeout *timeout)
+{
+ return protected_change_timeout (connection, timeout,
+ _dbus_timeout_list_add_timeout,
+ NULL, NULL, FALSE);
+}
+
+/**
+ * Removes a timeout using the connection's DBusRemoveTimeoutFunction
+ * if available. It's an error to call this function on a timeout
+ * that was not previously added.
+ * Connection lock should be held when calling this.
+ *
+ * @param connection the connection.
+ * @param timeout the timeout to remove.
+ */
+void
+_dbus_connection_remove_timeout_unlocked (DBusConnection *connection,
+ DBusTimeout *timeout)
+{
+ protected_change_timeout (connection, timeout,
+ NULL,
+ _dbus_timeout_list_remove_timeout,
+ NULL, FALSE);
+}
+
+/**
+ * Toggles a timeout and notifies app via connection's
+ * DBusTimeoutToggledFunction if available. It's an error to call this
+ * function on a timeout that was not previously added.
+ * Connection lock should be held when calling this.
+ *
+ * @param connection the connection.
+ * @param timeout the timeout to toggle.
+ * @param enabled whether to enable or disable
+ */
+void
+_dbus_connection_toggle_timeout_unlocked (DBusConnection *connection,
+ DBusTimeout *timeout,
+ dbus_bool_t enabled)
+{
+ protected_change_timeout (connection, timeout,
+ NULL, NULL,
+ _dbus_timeout_list_toggle_timeout,
+ enabled);
+}
+
+static dbus_bool_t
+_dbus_connection_attach_pending_call_unlocked (DBusConnection *connection,
+ DBusPendingCall *pending)
+{
+ dbus_uint32_t reply_serial;
+ DBusTimeout *timeout;
+
+ HAVE_LOCK_CHECK (connection);
+
+ reply_serial = _dbus_pending_call_get_reply_serial_unlocked (pending);
+
+ _dbus_assert (reply_serial != 0);
+
+ timeout = _dbus_pending_call_get_timeout_unlocked (pending);
+
+ if (timeout)
+ {
+ if (!_dbus_connection_add_timeout_unlocked (connection, timeout))
+ return FALSE;
+
+ if (!_dbus_hash_table_insert_int (connection->pending_replies,
+ reply_serial,
+ pending))
+ {
+ _dbus_connection_remove_timeout_unlocked (connection, timeout);
+
+ _dbus_pending_call_set_timeout_added_unlocked (pending, FALSE);
+ HAVE_LOCK_CHECK (connection);
+ return FALSE;
+ }
+
+ _dbus_pending_call_set_timeout_added_unlocked (pending, TRUE);
+ }
+ else
+ {
+ if (!_dbus_hash_table_insert_int (connection->pending_replies,
+ reply_serial,
+ pending))
+ {
+ HAVE_LOCK_CHECK (connection);
+ return FALSE;
+ }
+ }
+
+ _dbus_pending_call_ref_unlocked (pending);
+
+ HAVE_LOCK_CHECK (connection);
+
+ return TRUE;
+}
+
+static void
+free_pending_call_on_hash_removal (void *data)
+{
+ DBusPendingCall *pending;
+ DBusConnection *connection;
+
+ if (data == NULL)
+ return;
+
+ pending = data;
+
+ connection = _dbus_pending_call_get_connection_unlocked (pending);
+
+ HAVE_LOCK_CHECK (connection);
+
+ if (_dbus_pending_call_is_timeout_added_unlocked (pending))
+ {
+ _dbus_connection_remove_timeout_unlocked (connection,
+ _dbus_pending_call_get_timeout_unlocked (pending));
+
+ _dbus_pending_call_set_timeout_added_unlocked (pending, FALSE);
+ }
+
+ /* FIXME 1.0? this is sort of dangerous and undesirable to drop the lock
+ * here, but the pending call finalizer could in principle call out to
+ * application code so we pretty much have to... some larger code reorg
+ * might be needed.
+ */
+ _dbus_connection_ref_unlocked (connection);
+ _dbus_pending_call_unref_and_unlock (pending);
+ CONNECTION_LOCK (connection);
+ _dbus_connection_unref_unlocked (connection);
+}
+
+static void
+_dbus_connection_detach_pending_call_unlocked (DBusConnection *connection,
+ DBusPendingCall *pending)
+{
+ /* This ends up unlocking to call the pending call finalizer, which is unexpected to
+ * say the least.
+ */
+ _dbus_hash_table_remove_int (connection->pending_replies,
+ _dbus_pending_call_get_reply_serial_unlocked (pending));
+}
+
+static void
+_dbus_connection_detach_pending_call_and_unlock (DBusConnection *connection,
+ DBusPendingCall *pending)
+{
+ /* The idea here is to avoid finalizing the pending call
+ * with the lock held, since there's a destroy notifier
+ * in pending call that goes out to application code.
+ *
+ * There's an extra unlock inside the hash table
+ * "free pending call" function FIXME...
+ */
+ _dbus_pending_call_ref_unlocked (pending);
+ _dbus_hash_table_remove_int (connection->pending_replies,
+ _dbus_pending_call_get_reply_serial_unlocked (pending));
+
+ if (_dbus_pending_call_is_timeout_added_unlocked (pending))
+ _dbus_connection_remove_timeout_unlocked (connection,
+ _dbus_pending_call_get_timeout_unlocked (pending));
+
+ _dbus_pending_call_set_timeout_added_unlocked (pending, FALSE);
+
+ _dbus_pending_call_unref_and_unlock (pending);
+}
+
+/**
+ * Removes a pending call from the connection, such that
+ * the pending reply will be ignored. May drop the last
+ * reference to the pending call.
+ *
+ * @param connection the connection
+ * @param pending the pending call
+ */
+void
+_dbus_connection_remove_pending_call (DBusConnection *connection,
+ DBusPendingCall *pending)
+{
+ CONNECTION_LOCK (connection);
+ _dbus_connection_detach_pending_call_and_unlock (connection, pending);
+}
+
+/**
+ * Acquire the transporter I/O path. This must be done before
+ * doing any I/O in the transporter. May sleep and drop the
+ * IO path mutex while waiting for the I/O path.
+ *
+ * @param connection the connection.
+ * @param timeout_milliseconds maximum blocking time, or -1 for no limit.
+ * @returns TRUE if the I/O path was acquired.
+ */
+static dbus_bool_t
+_dbus_connection_acquire_io_path (DBusConnection *connection,
+ int timeout_milliseconds)
+{
+ dbus_bool_t we_acquired;
+
+ HAVE_LOCK_CHECK (connection);
+
+ /* We don't want the connection to vanish */
+ _dbus_connection_ref_unlocked (connection);
+
+ /* We will only touch io_path_acquired which is protected by our mutex */
+ CONNECTION_UNLOCK (connection);
+
+ _dbus_verbose ("locking io_path_mutex\n");
+ _dbus_cmutex_lock (connection->io_path_mutex);
+
+ _dbus_verbose ("start connection->io_path_acquired = %d timeout = %d\n",
+ connection->io_path_acquired, timeout_milliseconds);
+
+ we_acquired = FALSE;
+
+ if (connection->io_path_acquired)
+ {
+ if (timeout_milliseconds != -1)
+ {
+ _dbus_verbose ("waiting %d for IO path to be acquirable\n",
+ timeout_milliseconds);
+
+ if (!_dbus_condvar_wait_timeout (connection->io_path_cond,
+ connection->io_path_mutex,
+ timeout_milliseconds))
+ {
+ /* We timed out before anyone signaled. */
+ /* (writing the loop to handle the !timedout case by
+ * waiting longer if needed is a pain since dbus
+ * wraps pthread_cond_timedwait to take a relative
+ * time instead of absolute, something kind of stupid
+ * on our part. for now it doesn't matter, we will just
+ * end up back here eventually.)
+ */
+ }
+ }
+ else
+ {
+ while (connection->io_path_acquired)
+ {
+ _dbus_verbose ("waiting for IO path to be acquirable\n");
+ _dbus_condvar_wait (connection->io_path_cond,
+ connection->io_path_mutex);
+ }
+ }
+ }
+
+ if (!connection->io_path_acquired)
+ {
+ we_acquired = TRUE;
+ connection->io_path_acquired = TRUE;
+ }
+
+ _dbus_verbose ("end connection->io_path_acquired = %d we_acquired = %d\n",
+ connection->io_path_acquired, we_acquired);
+
+ _dbus_verbose ("unlocking io_path_mutex\n");
+ _dbus_cmutex_unlock (connection->io_path_mutex);
+
+ CONNECTION_LOCK (connection);
+
+ HAVE_LOCK_CHECK (connection);
+
+ _dbus_connection_unref_unlocked (connection);
+
+ return we_acquired;
+}
+
+/**
+ * Release the I/O path when you're done with it. Only call
+ * after you've acquired the I/O. Wakes up at most one thread
+ * currently waiting to acquire the I/O path.
+ *
+ * @param connection the connection.
+ */
+static void
+_dbus_connection_release_io_path (DBusConnection *connection)
+{
+ HAVE_LOCK_CHECK (connection);
+
+ _dbus_verbose ("locking io_path_mutex\n");
+ _dbus_cmutex_lock (connection->io_path_mutex);
+
+ _dbus_assert (connection->io_path_acquired);
+
+ _dbus_verbose ("start connection->io_path_acquired = %d\n",
+ connection->io_path_acquired);
+
+ connection->io_path_acquired = FALSE;
+ _dbus_condvar_wake_one (connection->io_path_cond);
+
+ _dbus_verbose ("unlocking io_path_mutex\n");
+ _dbus_cmutex_unlock (connection->io_path_mutex);
+}
+
+/**
+ * Queues incoming messages and sends outgoing messages for this
+ * connection, optionally blocking in the process. Each call to
+ * _dbus_connection_do_iteration_unlocked() will call select() or poll() one
+ * time and then read or write data if possible.
+ *
+ * The purpose of this function is to be able to flush outgoing
+ * messages or queue up incoming messages without returning
+ * control to the application and causing reentrancy weirdness.
+ *
+ * The flags parameter allows you to specify whether to
+ * read incoming messages, write outgoing messages, or both,
+ * and whether to block if no immediate action is possible.
+ *
+ * The timeout_milliseconds parameter does nothing unless the
+ * iteration is blocking.
+ *
+ * If there are no outgoing messages and DBUS_ITERATION_DO_READING
+ * wasn't specified, then it's impossible to block, even if
+ * you specify DBUS_ITERATION_BLOCK; in that case the function
+ * returns immediately.
+ *
+ * If pending is not NULL then a check is made if the pending call
+ * is completed after the io path has been required. If the call
+ * has been completed nothing is done. This must be done since
+ * the _dbus_connection_acquire_io_path releases the connection
+ * lock for a while.
+ *
+ * Called with connection lock held.
+ *
+ * @param connection the connection.
+ * @param pending the pending call that should be checked or NULL
+ * @param flags iteration flags.
+ * @param timeout_milliseconds maximum blocking time, or -1 for no limit.
+ */
+void
+_dbus_connection_do_iteration_unlocked (DBusConnection *connection,
+ DBusPendingCall *pending,
+ unsigned int flags,
+ int timeout_milliseconds)
+{
+ _dbus_verbose ("start\n");
+
+ HAVE_LOCK_CHECK (connection);
+
+ if (connection->n_outgoing == 0)
+ flags &= ~DBUS_ITERATION_DO_WRITING;
+
+ if (_dbus_connection_acquire_io_path (connection,
+ (flags & DBUS_ITERATION_BLOCK) ? timeout_milliseconds : 0))
+ {
+ HAVE_LOCK_CHECK (connection);
+
+ if ( (pending != NULL) && _dbus_pending_call_get_completed_unlocked(pending))
+ {
+ _dbus_verbose ("pending call completed while acquiring I/O path");
+ }
+ else if ( (pending != NULL) &&
+ _dbus_connection_peek_for_reply_unlocked (connection,
+ _dbus_pending_call_get_reply_serial_unlocked (pending)))
+ {
+ _dbus_verbose ("pending call completed while acquiring I/O path (reply found in queue)");
+ }
+ else
+ {
+ _dbus_transport_do_iteration (connection->transport,
+ flags, timeout_milliseconds);
+ }
+
+ _dbus_connection_release_io_path (connection);
+ }
+
+ HAVE_LOCK_CHECK (connection);
+
+ _dbus_verbose ("end\n");
+}
+
+/**
+ * Creates a new connection for the given transport. A transport
+ * represents a message stream that uses some concrete mechanism, such
+ * as UNIX domain sockets. May return #NULL if insufficient
+ * memory exists to create the connection.
+ *
+ * @param transport the transport.
+ * @returns the new connection, or #NULL on failure.
+ */
+DBusConnection*
+_dbus_connection_new_for_transport (DBusTransport *transport)
+{
+ DBusConnection *connection;
+ DBusWatchList *watch_list;
+ DBusTimeoutList *timeout_list;
+ DBusHashTable *pending_replies;
+ DBusList *disconnect_link;
+ DBusMessage *disconnect_message;
+ DBusCounter *outgoing_counter;
+ DBusObjectTree *objects;
+
+ watch_list = NULL;
+ connection = NULL;
+ pending_replies = NULL;
+ timeout_list = NULL;
+ disconnect_link = NULL;
+ disconnect_message = NULL;
+ outgoing_counter = NULL;
+ objects = NULL;
+
+ watch_list = _dbus_watch_list_new ();
+ if (watch_list == NULL)
+ goto error;
+
+ timeout_list = _dbus_timeout_list_new ();
+ if (timeout_list == NULL)
+ goto error;
+
+ pending_replies =
+ _dbus_hash_table_new (DBUS_HASH_INT,
+ NULL,
+ (DBusFreeFunction)free_pending_call_on_hash_removal);
+ if (pending_replies == NULL)
+ goto error;
+
+ connection = dbus_new0 (DBusConnection, 1);
+ if (connection == NULL)
+ goto error;
+
+ _dbus_rmutex_new_at_location (&connection->mutex);
+ if (connection->mutex == NULL)
+ goto error;
+
+ _dbus_cmutex_new_at_location (&connection->io_path_mutex);
+ if (connection->io_path_mutex == NULL)
+ goto error;
+
+ _dbus_cmutex_new_at_location (&connection->dispatch_mutex);
+ if (connection->dispatch_mutex == NULL)
+ goto error;
+
+ _dbus_condvar_new_at_location (&connection->dispatch_cond);
+ if (connection->dispatch_cond == NULL)
+ goto error;
+
+ _dbus_condvar_new_at_location (&connection->io_path_cond);
+ if (connection->io_path_cond == NULL)
+ goto error;
+
+ _dbus_rmutex_new_at_location (&connection->slot_mutex);
+ if (connection->slot_mutex == NULL)
+ goto error;
+
+ disconnect_message = dbus_message_new_signal (DBUS_PATH_LOCAL,
+ DBUS_INTERFACE_LOCAL,
+ "Disconnected");
+
+ if (disconnect_message == NULL)
+ goto error;
+
+ disconnect_link = _dbus_list_alloc_link (disconnect_message);
+ if (disconnect_link == NULL)
+ goto error;
+
+ outgoing_counter = _dbus_counter_new ();
+ if (outgoing_counter == NULL)
+ goto error;
+
+ objects = _dbus_object_tree_new (connection);
+ if (objects == NULL)
+ goto error;
+
+ if (_dbus_atomic_get (&_dbus_modify_sigpipe) != 0)
+ _dbus_disable_sigpipe ();
+
+ /* initialized to 0: use atomic op to avoid mixing atomic and non-atomic */
+ _dbus_atomic_inc (&connection->refcount);
+ connection->transport = transport;
+ connection->watches = watch_list;
+ connection->timeouts = timeout_list;
+ connection->pending_replies = pending_replies;
+ connection->outgoing_counter = outgoing_counter;
+ connection->filter_list = NULL;
+ connection->last_dispatch_status = DBUS_DISPATCH_COMPLETE; /* so we're notified first time there's data */
+ connection->objects = objects;
+ connection->exit_on_disconnect = FALSE;
+ connection->shareable = FALSE;
+ connection->builtin_filters_enabled = TRUE;
+ connection->route_peer_messages = FALSE;
+ connection->disconnected_message_arrived = FALSE;
+ connection->disconnected_message_processed = FALSE;
+
+#if defined(DBUS_ENABLE_CHECKS) || defined(DBUS_ENABLE_ASSERT)
+ connection->generation = _dbus_current_generation;
+#endif
+
+ _dbus_data_slot_list_init (&connection->slot_list);
+
+ connection->client_serial = 1;
+
+ connection->disconnect_message_link = disconnect_link;
+
+ CONNECTION_LOCK (connection);
+
+ if (!_dbus_transport_set_connection (transport, connection))
+ {
+ CONNECTION_UNLOCK (connection);
+
+ goto error;
+ }
+
+ _dbus_transport_ref (transport);
+
+ CONNECTION_UNLOCK (connection);
+
+ _dbus_connection_trace_ref (connection, 0, 1, "new_for_transport");
+ return connection;
+
+ error:
+ if (disconnect_message != NULL)
+ dbus_message_unref (disconnect_message);
+
+ if (disconnect_link != NULL)
+ _dbus_list_free_link (disconnect_link);
+
+ if (connection != NULL)
+ {
+ _dbus_condvar_free_at_location (&connection->io_path_cond);
+ _dbus_condvar_free_at_location (&connection->dispatch_cond);
+ _dbus_rmutex_free_at_location (&connection->mutex);
+ _dbus_cmutex_free_at_location (&connection->io_path_mutex);
+ _dbus_cmutex_free_at_location (&connection->dispatch_mutex);
+ _dbus_rmutex_free_at_location (&connection->slot_mutex);
+ dbus_free (connection);
+ }
+ if (pending_replies)
+ _dbus_hash_table_unref (pending_replies);
+
+ if (watch_list)
+ _dbus_watch_list_free (watch_list);
+
+ if (timeout_list)
+ _dbus_timeout_list_free (timeout_list);
+
+ if (outgoing_counter)
+ _dbus_counter_unref (outgoing_counter);
+
+ if (objects)
+ _dbus_object_tree_unref (objects);
+
+ return NULL;
+}
+
+/**
+ * Increments the reference count of a DBusConnection.
+ * Requires that the caller already holds the connection lock.
+ *
+ * @param connection the connection.
+ * @returns the connection.
+ */
+DBusConnection *
+_dbus_connection_ref_unlocked (DBusConnection *connection)
+{
+ dbus_int32_t old_refcount;
+
+ _dbus_assert (connection != NULL);
+ _dbus_assert (connection->generation == _dbus_current_generation);
+
+ HAVE_LOCK_CHECK (connection);
+
+ old_refcount = _dbus_atomic_inc (&connection->refcount);
+ _dbus_connection_trace_ref (connection, old_refcount, old_refcount + 1,
+ "ref_unlocked");
+
+ return connection;
+}
+
+/**
+ * Decrements the reference count of a DBusConnection.
+ * Requires that the caller already holds the connection lock.
+ *
+ * @param connection the connection.
+ */
+void
+_dbus_connection_unref_unlocked (DBusConnection *connection)
+{
+ dbus_int32_t old_refcount;
+
+ HAVE_LOCK_CHECK (connection);
+
+ _dbus_assert (connection != NULL);
+
+ old_refcount = _dbus_atomic_dec (&connection->refcount);
+
+ _dbus_connection_trace_ref (connection, old_refcount, old_refcount - 1,
+ "unref_unlocked");
+
+ if (old_refcount == 1)
+ _dbus_connection_last_unref (connection);
+}
+
+/**
+ * Allocate and return the next non-zero serial number for outgoing messages.
+ *
+ * This method is only valid to call from single-threaded code, such as
+ * the dbus-daemon, or with the connection lock held.
+ *
+ * @param connection the connection
+ * @returns A suitable serial number for the next message to be sent on the connection.
+ */
+dbus_uint32_t
+_dbus_connection_get_next_client_serial (DBusConnection *connection)
+{
+ dbus_uint32_t serial;
+
+ serial = connection->client_serial++;
+
+ if (connection->client_serial == 0)
+ connection->client_serial = 1;
+
+ return serial;
+}
+
+/**
+ * A callback for use with dbus_watch_new() to create a DBusWatch.
+ *
+ * @todo This is basically a hack - we could delete _dbus_transport_handle_watch()
+ * and the virtual handle_watch in DBusTransport if we got rid of it.
+ * The reason this is some work is threading, see the _dbus_connection_handle_watch()
+ * implementation.
+ *
+ * @param watch the watch.
+ * @param condition the current condition of the file descriptors being watched.
+ * @param data must be a pointer to a #DBusConnection
+ * @returns #FALSE if the IO condition may not have been fully handled due to lack of memory
+ */
+dbus_bool_t
+_dbus_connection_handle_watch (DBusWatch *watch,
+ unsigned int condition,
+ void *data)
+{
+ DBusConnection *connection;
+ dbus_bool_t retval;
+ DBusDispatchStatus status;
+
+ connection = data;
+
+ _dbus_verbose ("start\n");
+
+ CONNECTION_LOCK (connection);
+
+ if (!_dbus_connection_acquire_io_path (connection, 1))
+ {
+ /* another thread is handling the message */
+ CONNECTION_UNLOCK (connection);
+ return TRUE;
+ }
+
+ HAVE_LOCK_CHECK (connection);
+ retval = _dbus_transport_handle_watch (connection->transport,
+ watch, condition);
+
+ _dbus_connection_release_io_path (connection);
+
+ HAVE_LOCK_CHECK (connection);
+
+ _dbus_verbose ("middle\n");
+
+ status = _dbus_connection_get_dispatch_status_unlocked (connection);
+
+ /* this calls out to user code */
+ _dbus_connection_update_dispatch_status_and_unlock (connection, status);
+
+ _dbus_verbose ("end\n");
+
+ return retval;
+}
+
+/* Protected by _DBUS_LOCK (shared_connections) */
+static DBusHashTable *shared_connections = NULL;
+static DBusList *shared_connections_no_guid = NULL;
+
+static void
+close_connection_on_shutdown (DBusConnection *connection)
+{
+ DBusMessage *message;
+
+ dbus_connection_ref (connection);
+ _dbus_connection_close_possibly_shared (connection);
+
+ /* Churn through to the Disconnected message */
+ while ((message = dbus_connection_pop_message (connection)))
+ {
+ dbus_message_unref (message);
+ }
+ dbus_connection_unref (connection);
+}
+
+static void
+shared_connections_shutdown (void *data)
+{
+ int n_entries;
+
+ if (!_DBUS_LOCK (shared_connections))
+ {
+ /* We'd have initialized locks before adding anything, so there
+ * can't be anything there. */
+ return;
+ }
+
+ /* This is a little bit unpleasant... better ideas? */
+ while ((n_entries = _dbus_hash_table_get_n_entries (shared_connections)) > 0)
+ {
+ DBusConnection *connection;
+ DBusHashIter iter;
+
+ _dbus_hash_iter_init (shared_connections, &iter);
+ _dbus_hash_iter_next (&iter);
+
+ connection = _dbus_hash_iter_get_value (&iter);
+
+ _DBUS_UNLOCK (shared_connections);
+ close_connection_on_shutdown (connection);
+ if (!_DBUS_LOCK (shared_connections))
+ _dbus_assert_not_reached ("global locks were already initialized");
+
+ /* The connection should now be dead and not in our hash ... */
+ _dbus_assert (_dbus_hash_table_get_n_entries (shared_connections) < n_entries);
+ }
+
+ _dbus_assert (_dbus_hash_table_get_n_entries (shared_connections) == 0);
+
+ _dbus_hash_table_unref (shared_connections);
+ shared_connections = NULL;
+
+ if (shared_connections_no_guid != NULL)
+ {
+ DBusConnection *connection;
+ connection = _dbus_list_pop_first (&shared_connections_no_guid);
+ while (connection != NULL)
+ {
+ _DBUS_UNLOCK (shared_connections);
+ close_connection_on_shutdown (connection);
+ if (!_DBUS_LOCK (shared_connections))
+ _dbus_assert_not_reached ("global locks were already initialized");
+ connection = _dbus_list_pop_first (&shared_connections_no_guid);
+ }
+ }
+
+ shared_connections_no_guid = NULL;
+
+ _DBUS_UNLOCK (shared_connections);
+}
+
+static dbus_bool_t
+connection_lookup_shared (DBusAddressEntry *entry,
+ DBusConnection **result)
+{
+ _dbus_verbose ("checking for existing connection\n");
+
+ *result = NULL;
+
+ if (!_DBUS_LOCK (shared_connections))
+ {
+ /* If it was shared, we'd have initialized global locks when we put
+ * it in shared_connections. */
+ return FALSE;
+ }
+
+ if (shared_connections == NULL)
+ {
+ _dbus_verbose ("creating shared_connections hash table\n");
+
+ shared_connections = _dbus_hash_table_new (DBUS_HASH_STRING,
+ dbus_free,
+ NULL);
+ if (shared_connections == NULL)
+ {
+ _DBUS_UNLOCK (shared_connections);
+ return FALSE;
+ }
+
+ if (!_dbus_register_shutdown_func (shared_connections_shutdown, NULL))
+ {
+ _dbus_hash_table_unref (shared_connections);
+ shared_connections = NULL;
+ _DBUS_UNLOCK (shared_connections);
+ return FALSE;
+ }
+
+ _dbus_verbose (" successfully created shared_connections\n");
+
+ _DBUS_UNLOCK (shared_connections);
+ return TRUE; /* no point looking up in the hash we just made */
+ }
+ else
+ {
+ const char *guid;
+
+ guid = dbus_address_entry_get_value (entry, "guid");
+
+ if (guid != NULL)
+ {
+ DBusConnection *connection;
+
+ connection = _dbus_hash_table_lookup_string (shared_connections,
+ guid);
+
+ if (connection)
+ {
+ /* The DBusConnection can't be finalized without taking
+ * the shared_connections lock to remove it from the
+ * hash. So it's safe to ref the connection here.
+ * However, it may be disconnected if the Disconnected
+ * message hasn't been processed yet, in which case we
+ * want to pretend it isn't in the hash and avoid
+ * returning it.
+ *
+ * The idea is to avoid ever returning a disconnected connection
+ * from dbus_connection_open(). We could just synchronously
+ * drop our shared ref to the connection on connection disconnect,
+ * and then assert here that the connection is connected, but
+ * that causes reentrancy headaches.
+ */
+ CONNECTION_LOCK (connection);
+ if (_dbus_connection_get_is_connected_unlocked (connection))
+ {
+ _dbus_connection_ref_unlocked (connection);
+ *result = connection;
+ _dbus_verbose ("looked up existing connection to server guid %s\n",
+ guid);
+ }
+ else
+ {
+ _dbus_verbose ("looked up existing connection to server guid %s but it was disconnected so ignoring it\n",
+ guid);
+ }
+ CONNECTION_UNLOCK (connection);
+ }
+ }
+
+ _DBUS_UNLOCK (shared_connections);
+ return TRUE;
+ }
+}
+
+static dbus_bool_t
+connection_record_shared_unlocked (DBusConnection *connection,
+ const char *guid)
+{
+ char *guid_key;
+ char *guid_in_connection;
+
+ HAVE_LOCK_CHECK (connection);
+ _dbus_assert (connection->server_guid == NULL);
+ _dbus_assert (connection->shareable);
+
+ /* get a hard ref on this connection, even if
+ * we won't in fact store it in the hash, we still
+ * need to hold a ref on it until it's disconnected.
+ */
+ _dbus_connection_ref_unlocked (connection);
+
+ if (guid == NULL)
+ {
+ if (!_DBUS_LOCK (shared_connections))
+ return FALSE;
+
+ if (!_dbus_list_prepend (&shared_connections_no_guid, connection))
+ {
+ _DBUS_UNLOCK (shared_connections);
+ return FALSE;
+ }
+
+ _DBUS_UNLOCK (shared_connections);
+ return TRUE; /* don't store in the hash */
+ }
+
+ /* A separate copy of the key is required in the hash table, because
+ * we don't have a lock on the connection when we are doing a hash
+ * lookup.
+ */
+
+ guid_key = _dbus_strdup (guid);
+ if (guid_key == NULL)
+ return FALSE;
+
+ guid_in_connection = _dbus_strdup (guid);
+ if (guid_in_connection == NULL)
+ {
+ dbus_free (guid_key);
+ return FALSE;
+ }
+
+ if (!_DBUS_LOCK (shared_connections))
+ {
+ dbus_free (guid_in_connection);
+ dbus_free (guid_key);
+ return FALSE;
+ }
+
+ _dbus_assert (shared_connections != NULL);
+
+ if (!_dbus_hash_table_insert_string (shared_connections,
+ guid_key, connection))
+ {
+ dbus_free (guid_key);
+ dbus_free (guid_in_connection);
+ _DBUS_UNLOCK (shared_connections);
+ return FALSE;
+ }
+
+ connection->server_guid = guid_in_connection;
+
+ _dbus_verbose ("stored connection to %s to be shared\n",
+ connection->server_guid);
+
+ _DBUS_UNLOCK (shared_connections);
+
+ _dbus_assert (connection->server_guid != NULL);
+
+ return TRUE;
+}
+
+static void
+connection_forget_shared_unlocked (DBusConnection *connection)
+{
+ HAVE_LOCK_CHECK (connection);
+
+ if (!connection->shareable)
+ return;
+
+ if (!_DBUS_LOCK (shared_connections))
+ {
+ /* If it was shared, we'd have initialized global locks when we put
+ * it in the table; so it can't be there. */
+ return;
+ }
+
+ if (connection->server_guid != NULL)
+ {
+ _dbus_verbose ("dropping connection to %s out of the shared table\n",
+ connection->server_guid);
+
+ if (!_dbus_hash_table_remove_string (shared_connections,
+ connection->server_guid))
+ _dbus_assert_not_reached ("connection was not in the shared table");
+
+ dbus_free (connection->server_guid);
+ connection->server_guid = NULL;
+ }
+ else
+ {
+ _dbus_list_remove (&shared_connections_no_guid, connection);
+ }
+
+ _DBUS_UNLOCK (shared_connections);
+
+ /* remove our reference held on all shareable connections */
+ _dbus_connection_unref_unlocked (connection);
+}
+
+static DBusConnection*
+connection_try_from_address_entry (DBusAddressEntry *entry,
+ DBusError *error)
+{
+ DBusTransport *transport;
+ DBusConnection *connection;
+
+ transport = _dbus_transport_open (entry, error);
+
+ if (transport == NULL)
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ return NULL;
+ }
+
+ connection = _dbus_connection_new_for_transport (transport);
+
+ _dbus_transport_unref (transport);
+
+ if (connection == NULL)
+ {
+ _DBUS_SET_OOM (error);
+ return NULL;
+ }
+
+#ifndef DBUS_DISABLE_CHECKS
+ _dbus_assert (!connection->have_connection_lock);
+#endif
+ return connection;
+}
+
+/*
+ * If the shared parameter is true, then any existing connection will
+ * be used (and if a new connection is created, it will be available
+ * for use by others). If the shared parameter is false, a new
+ * connection will always be created, and the new connection will
+ * never be returned to other callers.
+ *
+ * @param address the address
+ * @param shared whether the connection is shared or private
+ * @param error error return
+ * @returns the connection or #NULL on error
+ */
+static DBusConnection*
+_dbus_connection_open_internal (const char *address,
+ dbus_bool_t shared,
+ DBusError *error)
+{
+ DBusConnection *connection;
+ DBusAddressEntry **entries;
+ DBusError tmp_error = DBUS_ERROR_INIT;
+ DBusError first_error = DBUS_ERROR_INIT;
+ int len, i;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ _dbus_verbose ("opening %s connection to: %s\n",
+ shared ? "shared" : "private", address);
+
+ if (!dbus_parse_address (address, &entries, &len, error))
+ return NULL;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ connection = NULL;
+
+ for (i = 0; i < len; i++)
+ {
+ if (shared)
+ {
+ if (!connection_lookup_shared (entries[i], &connection))
+ _DBUS_SET_OOM (&tmp_error);
+ }
+
+ if (connection == NULL)
+ {
+ connection = connection_try_from_address_entry (entries[i],
+ &tmp_error);
+
+ if (connection != NULL && shared)
+ {
+ const char *guid;
+
+ connection->shareable = TRUE;
+
+ /* guid may be NULL */
+ guid = dbus_address_entry_get_value (entries[i], "guid");
+
+ CONNECTION_LOCK (connection);
+
+ if (!connection_record_shared_unlocked (connection, guid))
+ {
+ _DBUS_SET_OOM (&tmp_error);
+ _dbus_connection_close_possibly_shared_and_unlock (connection);
+ dbus_connection_unref (connection);
+ connection = NULL;
+ }
+ else
+ CONNECTION_UNLOCK (connection);
+ }
+ }
+
+ if (connection)
+ break;
+
+ _DBUS_ASSERT_ERROR_IS_SET (&tmp_error);
+
+ if (i == 0)
+ dbus_move_error (&tmp_error, &first_error);
+ else
+ dbus_error_free (&tmp_error);
+ }
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+ _DBUS_ASSERT_ERROR_IS_CLEAR (&tmp_error);
+
+ if (connection == NULL)
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (&first_error);
+ dbus_move_error (&first_error, error);
+ }
+ else
+ dbus_error_free (&first_error);
+
+ dbus_address_entries_free (entries);
+ return connection;
+}
+
+/**
+ * Closes a shared OR private connection, while dbus_connection_close() can
+ * only be used on private connections. Should only be called by the
+ * dbus code that owns the connection - an owner must be known,
+ * the open/close state is like malloc/free, not like ref/unref.
+ *
+ * @param connection the connection
+ */
+void
+_dbus_connection_close_possibly_shared (DBusConnection *connection)
+{
+ _dbus_assert (connection != NULL);
+ _dbus_assert (connection->generation == _dbus_current_generation);
+
+ CONNECTION_LOCK (connection);
+ _dbus_connection_close_possibly_shared_and_unlock (connection);
+}
+
+static DBusPreallocatedSend*
+_dbus_connection_preallocate_send_unlocked (DBusConnection *connection)
+{
+ DBusPreallocatedSend *preallocated;
+
+ HAVE_LOCK_CHECK (connection);
+
+ _dbus_assert (connection != NULL);
+
+ preallocated = dbus_new (DBusPreallocatedSend, 1);
+ if (preallocated == NULL)
+ return NULL;
+
+ preallocated->queue_link = _dbus_list_alloc_link (NULL);
+ if (preallocated->queue_link == NULL)
+ goto failed_0;
+
+ preallocated->counter_link = _dbus_list_alloc_link (connection->outgoing_counter);
+ if (preallocated->counter_link == NULL)
+ goto failed_1;
+
+ _dbus_counter_ref (preallocated->counter_link->data);
+
+ preallocated->connection = connection;
+
+ return preallocated;
+
+ failed_1:
+ _dbus_list_free_link (preallocated->queue_link);
+ failed_0:
+ dbus_free (preallocated);
+
+ return NULL;
+}
+
+/* Called with lock held, does not update dispatch status */
+static void
+_dbus_connection_send_preallocated_unlocked_no_update (DBusConnection *connection,
+ DBusPreallocatedSend *preallocated,
+ DBusMessage *message,
+ dbus_uint32_t *client_serial)
+{
+ dbus_uint32_t serial;
+
+ preallocated->queue_link->data = message;
+ _dbus_list_prepend_link (&connection->outgoing_messages,
+ preallocated->queue_link);
+
+ /* It's OK that we'll never call the notify function, because for the
+ * outgoing limit, there isn't one */
+ _dbus_message_add_counter_link (message,
+ preallocated->counter_link);
+
+ dbus_free (preallocated);
+ preallocated = NULL;
+
+ dbus_message_ref (message);
+
+ connection->n_outgoing += 1;
+
+ _dbus_verbose ("Message %p (%s %s %s %s '%s') for %s added to outgoing queue %p, %d pending to send\n",
+ message,
+ dbus_message_type_to_string (dbus_message_get_type (message)),
+ dbus_message_get_path (message) ?
+ dbus_message_get_path (message) :
+ "no path",
+ dbus_message_get_interface (message) ?
+ dbus_message_get_interface (message) :
+ "no interface",
+ dbus_message_get_member (message) ?
+ dbus_message_get_member (message) :
+ "no member",
+ dbus_message_get_signature (message),
+ dbus_message_get_destination (message) ?
+ dbus_message_get_destination (message) :
+ "null",
+ connection,
+ connection->n_outgoing);
+
+ if (dbus_message_get_serial (message) == 0)
+ {
+ serial = _dbus_connection_get_next_client_serial (connection);
+ dbus_message_set_serial (message, serial);
+ if (client_serial)
+ *client_serial = serial;
+ }
+ else
+ {
+ if (client_serial)
+ *client_serial = dbus_message_get_serial (message);
+ }
+
+ _dbus_verbose ("Message %p serial is %u\n",
+ message, dbus_message_get_serial (message));
+
+ dbus_message_lock (message);
+
+ /* Now we need to run an iteration to hopefully just write the messages
+ * out immediately, and otherwise get them queued up
+ */
+ _dbus_connection_do_iteration_unlocked (connection,
+ NULL,
+ DBUS_ITERATION_DO_WRITING,
+ -1);
+
+ /* If stuff is still queued up, be sure we wake up the main loop */
+ if (connection->n_outgoing > 0)
+ _dbus_connection_wakeup_mainloop (connection);
+}
+
+static void
+_dbus_connection_send_preallocated_and_unlock (DBusConnection *connection,
+ DBusPreallocatedSend *preallocated,
+ DBusMessage *message,
+ dbus_uint32_t *client_serial)
+{
+ DBusDispatchStatus status;
+
+ HAVE_LOCK_CHECK (connection);
+
+ _dbus_connection_send_preallocated_unlocked_no_update (connection,
+ preallocated,
+ message, client_serial);
+
+ _dbus_verbose ("middle\n");
+ status = _dbus_connection_get_dispatch_status_unlocked (connection);
+
+ /* this calls out to user code */
+ _dbus_connection_update_dispatch_status_and_unlock (connection, status);
+}
+
+/**
+ * Like dbus_connection_send(), but assumes the connection
+ * is already locked on function entry, and unlocks before returning.
+ *
+ * @param connection the connection
+ * @param message the message to send
+ * @param client_serial return location for client serial of sent message
+ * @returns #FALSE on out-of-memory
+ */
+dbus_bool_t
+_dbus_connection_send_and_unlock (DBusConnection *connection,
+ DBusMessage *message,
+ dbus_uint32_t *client_serial)
+{
+ DBusPreallocatedSend *preallocated;
+
+ _dbus_assert (connection != NULL);
+ _dbus_assert (message != NULL);
+
+ preallocated = _dbus_connection_preallocate_send_unlocked (connection);
+ if (preallocated == NULL)
+ {
+ CONNECTION_UNLOCK (connection);
+ return FALSE;
+ }
+
+ _dbus_connection_send_preallocated_and_unlock (connection,
+ preallocated,
+ message,
+ client_serial);
+ return TRUE;
+}
+
+/**
+ * Used internally to handle the semantics of dbus_server_set_new_connection_function().
+ * If the new connection function does not ref the connection, we want to close it.
+ *
+ * A bit of a hack, probably the new connection function should have returned a value
+ * for whether to close, or should have had to close the connection itself if it
+ * didn't want it.
+ *
+ * But, this works OK as long as the new connection function doesn't do anything
+ * crazy like keep the connection around without ref'ing it.
+ *
+ * We have to lock the connection across refcount check and close in case
+ * the new connection function spawns a thread that closes and unrefs.
+ * In that case, if the app thread
+ * closes and unrefs first, we'll harmlessly close again; if the app thread
+ * still has the ref, we'll close and then the app will close harmlessly.
+ * If the app unrefs without closing, the app is broken since if the
+ * app refs from the new connection function it is supposed to also close.
+ *
+ * If we didn't atomically check the refcount and close with the lock held
+ * though, we could screw this up.
+ *
+ * @param connection the connection
+ */
+void
+_dbus_connection_close_if_only_one_ref (DBusConnection *connection)
+{
+ dbus_int32_t refcount;
+
+ CONNECTION_LOCK (connection);
+
+ refcount = _dbus_atomic_get (&connection->refcount);
+ /* The caller should have at least one ref */
+ _dbus_assert (refcount >= 1);
+
+ if (refcount == 1)
+ _dbus_connection_close_possibly_shared_and_unlock (connection);
+ else
+ CONNECTION_UNLOCK (connection);
+}
+
+
+/**
+ * When a function that blocks has been called with a timeout, and we
+ * run out of memory, the time to wait for memory is based on the
+ * timeout. If the caller was willing to block a long time we wait a
+ * relatively long time for memory, if they were only willing to block
+ * briefly then we retry for memory at a rapid rate.
+ *
+ * @param timeout_milliseconds the timeout requested for blocking
+ */
+static void
+_dbus_memory_pause_based_on_timeout (int timeout_milliseconds)
+{
+ if (timeout_milliseconds == -1)
+ _dbus_sleep_milliseconds (1000);
+ else if (timeout_milliseconds < 100)
+ ; /* just busy loop */
+ else if (timeout_milliseconds <= 1000)
+ _dbus_sleep_milliseconds (timeout_milliseconds / 3);
+ else
+ _dbus_sleep_milliseconds (1000);
+}
+
+static DBusMessage *
+generate_local_error_message (dbus_uint32_t serial,
+ const char *error_name,
+ const char *error_msg)
+{
+ DBusMessage *message;
+ message = dbus_message_new (DBUS_MESSAGE_TYPE_ERROR);
+ if (!message)
+ goto out;
+
+ if (!dbus_message_set_error_name (message, error_name))
+ {
+ dbus_message_unref (message);
+ message = NULL;
+ goto out;
+ }
+
+ dbus_message_set_no_reply (message, TRUE);
+
+ if (!dbus_message_set_reply_serial (message,
+ serial))
+ {
+ dbus_message_unref (message);
+ message = NULL;
+ goto out;
+ }
+
+ if (error_msg != NULL)
+ {
+ DBusMessageIter iter;
+
+ dbus_message_iter_init_append (message, &iter);
+ if (!dbus_message_iter_append_basic (&iter,
+ DBUS_TYPE_STRING,
+ &error_msg))
+ {
+ dbus_message_unref (message);
+ message = NULL;
+ goto out;
+ }
+ }
+
+ out:
+ return message;
+}
+
+/*
+ * Peek the incoming queue to see if we got reply for a specific serial
+ */
+static dbus_bool_t
+_dbus_connection_peek_for_reply_unlocked (DBusConnection *connection,
+ dbus_uint32_t client_serial)
+{
+ DBusList *link;
+ HAVE_LOCK_CHECK (connection);
+
+ link = _dbus_list_get_first_link (&connection->incoming_messages);
+
+ while (link != NULL)
+ {
+ DBusMessage *reply = link->data;
+
+ if (dbus_message_get_reply_serial (reply) == client_serial)
+ {
+ _dbus_verbose ("%s reply to %d found in queue\n", _DBUS_FUNCTION_NAME, client_serial);
+ return TRUE;
+ }
+ link = _dbus_list_get_next_link (&connection->incoming_messages, link);
+ }
+
+ return FALSE;
+}
+
+/* This is slightly strange since we can pop a message here without
+ * the dispatch lock.
+ */
+static DBusMessage*
+check_for_reply_unlocked (DBusConnection *connection,
+ dbus_uint32_t client_serial)
+{
+ DBusList *link;
+
+ HAVE_LOCK_CHECK (connection);
+
+ link = _dbus_list_get_first_link (&connection->incoming_messages);
+
+ while (link != NULL)
+ {
+ DBusMessage *reply = link->data;
+
+ if (dbus_message_get_reply_serial (reply) == client_serial)
+ {
+ _dbus_list_remove_link (&connection->incoming_messages, link);
+ connection->n_incoming -= 1;
+ return reply;
+ }
+ link = _dbus_list_get_next_link (&connection->incoming_messages, link);
+ }
+
+ return NULL;
+}
+
+static void
+connection_timeout_and_complete_all_pending_calls_unlocked (DBusConnection *connection)
+{
+ /* We can't iterate over the hash in the normal way since we'll be
+ * dropping the lock for each item. So we restart the
+ * iter each time as we drain the hash table.
+ */
+
+ while (_dbus_hash_table_get_n_entries (connection->pending_replies) > 0)
+ {
+ DBusPendingCall *pending;
+ DBusHashIter iter;
+
+ _dbus_hash_iter_init (connection->pending_replies, &iter);
+ _dbus_hash_iter_next (&iter);
+
+ pending = _dbus_hash_iter_get_value (&iter);
+ _dbus_pending_call_ref_unlocked (pending);
+
+ _dbus_pending_call_queue_timeout_error_unlocked (pending,
+ connection);
+
+ if (_dbus_pending_call_is_timeout_added_unlocked (pending))
+ _dbus_connection_remove_timeout_unlocked (connection,
+ _dbus_pending_call_get_timeout_unlocked (pending));
+ _dbus_pending_call_set_timeout_added_unlocked (pending, FALSE);
+ _dbus_hash_iter_remove_entry (&iter);
+
+ _dbus_pending_call_unref_and_unlock (pending);
+ CONNECTION_LOCK (connection);
+ }
+ HAVE_LOCK_CHECK (connection);
+}
+
+static void
+complete_pending_call_and_unlock (DBusConnection *connection,
+ DBusPendingCall *pending,
+ DBusMessage *message)
+{
+ _dbus_pending_call_set_reply_unlocked (pending, message);
+ _dbus_pending_call_ref_unlocked (pending); /* in case there's no app with a ref held */
+ _dbus_pending_call_start_completion_unlocked(pending);
+ _dbus_connection_detach_pending_call_and_unlock (connection, pending);
+
+ /* Must be called unlocked since it invokes app callback */
+ _dbus_pending_call_finish_completion (pending);
+ dbus_pending_call_unref (pending);
+}
+
+static dbus_bool_t
+check_for_reply_and_update_dispatch_unlocked (DBusConnection *connection,
+ DBusPendingCall *pending)
+{
+ DBusMessage *reply;
+ DBusDispatchStatus status;
+
+ reply = check_for_reply_unlocked (connection,
+ _dbus_pending_call_get_reply_serial_unlocked (pending));
+ if (reply != NULL)
+ {
+ _dbus_verbose ("checked for reply\n");
+
+ _dbus_verbose ("dbus_connection_send_with_reply_and_block(): got reply\n");
+
+ complete_pending_call_and_unlock (connection, pending, reply);
+ dbus_message_unref (reply);
+
+ CONNECTION_LOCK (connection);
+ status = _dbus_connection_get_dispatch_status_unlocked (connection);
+ _dbus_connection_update_dispatch_status_and_unlock (connection, status);
+ dbus_pending_call_unref (pending);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ * Blocks until a pending call times out or gets a reply.
+ *
+ * Does not re-enter the main loop or run filter/path-registered
+ * callbacks. The reply to the message will not be seen by
+ * filter callbacks.
+ *
+ * Returns immediately if pending call already got a reply.
+ *
+ * @todo could use performance improvements (it keeps scanning
+ * the whole message queue for example)
+ *
+ * @param pending the pending call we block for a reply on
+ */
+void
+_dbus_connection_block_pending_call (DBusPendingCall *pending)
+{
+ dbus_int64_t start_tv_sec;
+ long start_tv_usec;
+ dbus_int64_t tv_sec;
+ long tv_usec;
+ DBusDispatchStatus status;
+ DBusConnection *connection;
+ dbus_uint32_t client_serial;
+ DBusTimeout *timeout;
+ int timeout_milliseconds, elapsed_milliseconds;
+
+ _dbus_assert (pending != NULL);
+
+ if (dbus_pending_call_get_completed (pending))
+ return;
+
+ dbus_pending_call_ref (pending); /* necessary because the call could be canceled */
+
+ connection = _dbus_pending_call_get_connection_and_lock (pending);
+
+ /* Flush message queue - note, can affect dispatch status */
+ _dbus_connection_flush_unlocked (connection);
+
+ client_serial = _dbus_pending_call_get_reply_serial_unlocked (pending);
+
+ /* note that timeout_milliseconds is limited to a smallish value
+ * in _dbus_pending_call_new() so overflows aren't possible
+ * below
+ */
+ timeout = _dbus_pending_call_get_timeout_unlocked (pending);
+ _dbus_get_monotonic_time (&start_tv_sec, &start_tv_usec);
+ if (timeout)
+ {
+ timeout_milliseconds = dbus_timeout_get_interval (timeout);
+
+ _dbus_verbose ("dbus_connection_send_with_reply_and_block(): will block %d milliseconds for reply serial %u from %" DBUS_INT64_MODIFIER "d sec %ld usec\n",
+ timeout_milliseconds,
+ client_serial,
+ start_tv_sec, start_tv_usec);
+ }
+ else
+ {
+ timeout_milliseconds = -1;
+
+ _dbus_verbose ("dbus_connection_send_with_reply_and_block(): will block for reply serial %u\n", client_serial);
+ }
+
+ /* check to see if we already got the data off the socket */
+ /* from another blocked pending call */
+ if (check_for_reply_and_update_dispatch_unlocked (connection, pending))
+ return;
+
+ /* Now we wait... */
+ /* always block at least once as we know we don't have the reply yet */
+ _dbus_connection_do_iteration_unlocked (connection,
+ pending,
+ DBUS_ITERATION_DO_READING |
+ DBUS_ITERATION_BLOCK,
+ timeout_milliseconds);
+
+ recheck_status:
+
+ _dbus_verbose ("top of recheck\n");
+
+ HAVE_LOCK_CHECK (connection);
+
+ /* queue messages and get status */
+
+ status = _dbus_connection_get_dispatch_status_unlocked (connection);
+
+ /* the get_completed() is in case a dispatch() while we were blocking
+ * got the reply instead of us.
+ */
+ if (_dbus_pending_call_get_completed_unlocked (pending))
+ {
+ _dbus_verbose ("Pending call completed by dispatch\n");
+ _dbus_connection_update_dispatch_status_and_unlock (connection, status);
+ dbus_pending_call_unref (pending);
+ return;
+ }
+
+ if (status == DBUS_DISPATCH_DATA_REMAINS)
+ {
+ if (check_for_reply_and_update_dispatch_unlocked (connection, pending))
+ return;
+ }
+
+ _dbus_get_monotonic_time (&tv_sec, &tv_usec);
+ elapsed_milliseconds = (tv_sec - start_tv_sec) * 1000 +
+ (tv_usec - start_tv_usec) / 1000;
+
+ if (!_dbus_connection_get_is_connected_unlocked (connection))
+ {
+ DBusMessage *error_msg;
+
+ error_msg = generate_local_error_message (client_serial,
+ DBUS_ERROR_DISCONNECTED,
+ "Connection was disconnected before a reply was received");
+
+ /* on OOM error_msg is set to NULL */
+ complete_pending_call_and_unlock (connection, pending, error_msg);
+ if (error_msg != NULL)
+ dbus_message_unref (error_msg);
+ dbus_pending_call_unref (pending);
+ return;
+ }
+ else if (connection->disconnect_message_link == NULL)
+ _dbus_verbose ("dbus_connection_send_with_reply_and_block(): disconnected\n");
+ else if (timeout == NULL)
+ {
+ if (status == DBUS_DISPATCH_NEED_MEMORY)
+ {
+ /* Try sleeping a bit, as we aren't sure we need to block for reading,
+ * we may already have a reply in the buffer and just can't process
+ * it.
+ */
+ _dbus_verbose ("dbus_connection_send_with_reply_and_block() waiting for more memory\n");
+
+ _dbus_memory_pause_based_on_timeout (timeout_milliseconds - elapsed_milliseconds);
+ }
+ else
+ {
+ /* block again, we don't have the reply buffered yet. */
+ _dbus_connection_do_iteration_unlocked (connection,
+ pending,
+ DBUS_ITERATION_DO_READING |
+ DBUS_ITERATION_BLOCK,
+ timeout_milliseconds - elapsed_milliseconds);
+ }
+
+ goto recheck_status;
+ }
+ else if (tv_sec < start_tv_sec)
+ _dbus_verbose ("dbus_connection_send_with_reply_and_block(): clock set backward\n");
+ else if (elapsed_milliseconds < timeout_milliseconds)
+ {
+ _dbus_verbose ("dbus_connection_send_with_reply_and_block(): %d milliseconds remain\n", timeout_milliseconds - elapsed_milliseconds);
+
+ if (status == DBUS_DISPATCH_NEED_MEMORY)
+ {
+ /* Try sleeping a bit, as we aren't sure we need to block for reading,
+ * we may already have a reply in the buffer and just can't process
+ * it.
+ */
+ _dbus_verbose ("dbus_connection_send_with_reply_and_block() waiting for more memory\n");
+
+ _dbus_memory_pause_based_on_timeout (timeout_milliseconds - elapsed_milliseconds);
+ }
+ else
+ {
+ /* block again, we don't have the reply buffered yet. */
+ _dbus_connection_do_iteration_unlocked (connection,
+ pending,
+ DBUS_ITERATION_DO_READING |
+ DBUS_ITERATION_BLOCK,
+ timeout_milliseconds - elapsed_milliseconds);
+ }
+
+ goto recheck_status;
+ }
+
+ _dbus_verbose ("dbus_connection_send_with_reply_and_block(): Waited %d milliseconds and got no reply\n",
+ elapsed_milliseconds);
+
+ _dbus_assert (!_dbus_pending_call_get_completed_unlocked (pending));
+
+ /* unlock and call user code */
+ complete_pending_call_and_unlock (connection, pending, NULL);
+
+ /* update user code on dispatch status */
+ CONNECTION_LOCK (connection);
+ status = _dbus_connection_get_dispatch_status_unlocked (connection);
+ _dbus_connection_update_dispatch_status_and_unlock (connection, status);
+ dbus_pending_call_unref (pending);
+}
+
+/**
+ * Return how many file descriptors are pending in the loader
+ *
+ * @param connection the connection
+ */
+int
+_dbus_connection_get_pending_fds_count (DBusConnection *connection)
+{
+ return _dbus_transport_get_pending_fds_count (connection->transport);
+}
+
+/**
+ * Register a function to be called whenever the number of pending file
+ * descriptors in the loader change.
+ *
+ * @param connection the connection
+ * @param callback the callback
+ */
+void
+_dbus_connection_set_pending_fds_function (DBusConnection *connection,
+ DBusPendingFdsChangeFunction callback,
+ void *data)
+{
+ _dbus_transport_set_pending_fds_function (connection->transport,
+ callback, data);
+}
+
+/** @} */
+
+/**
+ * @addtogroup DBusConnection
+ *
+ * @{
+ */
+
+/**
+ * Gets a connection to a remote address. If a connection to the given
+ * address already exists, returns the existing connection with its
+ * reference count incremented. Otherwise, returns a new connection
+ * and saves the new connection for possible re-use if a future call
+ * to dbus_connection_open() asks to connect to the same server.
+ *
+ * Use dbus_connection_open_private() to get a dedicated connection
+ * not shared with other callers of dbus_connection_open().
+ *
+ * If the open fails, the function returns #NULL, and provides a
+ * reason for the failure in the error parameter. Pass #NULL for the
+ * error parameter if you aren't interested in the reason for
+ * failure.
+ *
+ * Because this connection is shared, no user of the connection
+ * may call dbus_connection_close(). However, when you are done with the
+ * connection you should call dbus_connection_unref().
+ *
+ * @note Prefer dbus_connection_open() to dbus_connection_open_private()
+ * unless you have good reason; connections are expensive enough
+ * that it's wasteful to create lots of connections to the same
+ * server.
+ *
+ * @param address the address.
+ * @param error address where an error can be returned.
+ * @returns new connection, or #NULL on failure.
+ */
+DBusConnection*
+dbus_connection_open (const char *address,
+ DBusError *error)
+{
+ DBusConnection *connection;
+
+ _dbus_return_val_if_fail (address != NULL, NULL);
+ _dbus_return_val_if_error_is_set (error, NULL);
+
+ connection = _dbus_connection_open_internal (address,
+ TRUE,
+ error);
+
+ return connection;
+}
+
+/**
+ * Opens a new, dedicated connection to a remote address. Unlike
+ * dbus_connection_open(), always creates a new connection.
+ * This connection will not be saved or recycled by libdbus.
+ *
+ * If the open fails, the function returns #NULL, and provides a
+ * reason for the failure in the error parameter. Pass #NULL for the
+ * error parameter if you aren't interested in the reason for
+ * failure.
+ *
+ * When you are done with this connection, you must
+ * dbus_connection_close() to disconnect it,
+ * and dbus_connection_unref() to free the connection object.
+ *
+ * (The dbus_connection_close() can be skipped if the
+ * connection is already known to be disconnected, for example
+ * if you are inside a handler for the Disconnected signal.)
+ *
+ * @note Prefer dbus_connection_open() to dbus_connection_open_private()
+ * unless you have good reason; connections are expensive enough
+ * that it's wasteful to create lots of connections to the same
+ * server.
+ *
+ * @param address the address.
+ * @param error address where an error can be returned.
+ * @returns new connection, or #NULL on failure.
+ */
+DBusConnection*
+dbus_connection_open_private (const char *address,
+ DBusError *error)
+{
+ DBusConnection *connection;
+
+ _dbus_return_val_if_fail (address != NULL, NULL);
+ _dbus_return_val_if_error_is_set (error, NULL);
+
+ connection = _dbus_connection_open_internal (address,
+ FALSE,
+ error);
+
+ return connection;
+}
+
+/**
+ * Increments the reference count of a DBusConnection.
+ *
+ * @param connection the connection.
+ * @returns the connection.
+ */
+DBusConnection *
+dbus_connection_ref (DBusConnection *connection)
+{
+ dbus_int32_t old_refcount;
+
+ _dbus_return_val_if_fail (connection != NULL, NULL);
+ _dbus_return_val_if_fail (connection->generation == _dbus_current_generation, NULL);
+ old_refcount = _dbus_atomic_inc (&connection->refcount);
+ _dbus_connection_trace_ref (connection, old_refcount, old_refcount + 1,
+ "ref");
+
+ return connection;
+}
+
+static void
+free_outgoing_message (void *element,
+ void *data)
+{
+ DBusMessage *message = element;
+ DBusConnection *connection = data;
+
+ _dbus_message_remove_counter (message, connection->outgoing_counter);
+ dbus_message_unref (message);
+}
+
+/* This is run without the mutex held, but after the last reference
+ * to the connection has been dropped we should have no thread-related
+ * problems
+ */
+static void
+_dbus_connection_last_unref (DBusConnection *connection)
+{
+ DBusList *link;
+
+ _dbus_verbose ("Finalizing connection %p\n", connection);
+
+ _dbus_assert (_dbus_atomic_get (&connection->refcount) == 0);
+
+ /* You have to disconnect the connection before unref:ing it. Otherwise
+ * you won't get the disconnected message.
+ */
+ _dbus_assert (!_dbus_transport_get_is_connected (connection->transport));
+ _dbus_assert (connection->server_guid == NULL);
+
+ /* ---- We're going to call various application callbacks here, hope it doesn't break anything... */
+ _dbus_object_tree_free_all_unlocked (connection->objects);
+
+ dbus_connection_set_dispatch_status_function (connection, NULL, NULL, NULL);
+ dbus_connection_set_wakeup_main_function (connection, NULL, NULL, NULL);
+ dbus_connection_set_unix_user_function (connection, NULL, NULL, NULL);
+ dbus_connection_set_windows_user_function (connection, NULL, NULL, NULL);
+
+ _dbus_watch_list_free (connection->watches);
+ connection->watches = NULL;
+
+ _dbus_timeout_list_free (connection->timeouts);
+ connection->timeouts = NULL;
+
+ _dbus_data_slot_list_free (&connection->slot_list);
+
+ link = _dbus_list_get_first_link (&connection->filter_list);
+ while (link != NULL)
+ {
+ DBusMessageFilter *filter = link->data;
+ DBusList *next = _dbus_list_get_next_link (&connection->filter_list, link);
+
+ filter->function = NULL;
+ _dbus_message_filter_unref (filter); /* calls app callback */
+ link->data = NULL;
+
+ link = next;
+ }
+ _dbus_list_clear (&connection->filter_list);
+
+ /* ---- Done with stuff that invokes application callbacks */
+
+ _dbus_object_tree_unref (connection->objects);
+
+ _dbus_hash_table_unref (connection->pending_replies);
+ connection->pending_replies = NULL;
+
+ _dbus_list_foreach (&connection->outgoing_messages,
+ free_outgoing_message,
+ connection);
+ _dbus_list_clear (&connection->outgoing_messages);
+
+ _dbus_list_clear_full (&connection->incoming_messages,
+ (DBusFreeFunction) dbus_message_unref);
+
+ _dbus_counter_unref (connection->outgoing_counter);
+
+ _dbus_transport_unref (connection->transport);
+
+ if (connection->disconnect_message_link)
+ {
+ DBusMessage *message = connection->disconnect_message_link->data;
+ dbus_message_unref (message);
+ _dbus_list_free_link (connection->disconnect_message_link);
+ }
+
+ _dbus_condvar_free_at_location (&connection->dispatch_cond);
+ _dbus_condvar_free_at_location (&connection->io_path_cond);
+
+ _dbus_cmutex_free_at_location (&connection->io_path_mutex);
+ _dbus_cmutex_free_at_location (&connection->dispatch_mutex);
+
+ _dbus_rmutex_free_at_location (&connection->slot_mutex);
+
+ _dbus_rmutex_free_at_location (&connection->mutex);
+
+ dbus_free (connection);
+}
+
+/**
+ * Decrements the reference count of a DBusConnection, and finalizes
+ * it if the count reaches zero.
+ *
+ * Note: it is a bug to drop the last reference to a connection that
+ * is still connected.
+ *
+ * For shared connections, libdbus will own a reference
+ * as long as the connection is connected, so you can know that either
+ * you don't have the last reference, or it's OK to drop the last reference.
+ * Most connections are shared. dbus_connection_open() and dbus_bus_get()
+ * return shared connections.
+ *
+ * For private connections, the creator of the connection must arrange for
+ * dbus_connection_close() to be called prior to dropping the last reference.
+ * Private connections come from dbus_connection_open_private() or dbus_bus_get_private().
+ *
+ * @param connection the connection.
+ */
+void
+dbus_connection_unref (DBusConnection *connection)
+{
+ dbus_int32_t old_refcount;
+
+ _dbus_return_if_fail (connection != NULL);
+ _dbus_return_if_fail (connection->generation == _dbus_current_generation);
+
+ old_refcount = _dbus_atomic_dec (&connection->refcount);
+
+ _dbus_connection_trace_ref (connection, old_refcount, old_refcount - 1,
+ "unref");
+
+ if (old_refcount == 1)
+ {
+#ifndef DBUS_DISABLE_CHECKS
+ if (_dbus_transport_get_is_connected (connection->transport))
+ {
+ _dbus_warn_check_failed ("The last reference on a connection was dropped without closing the connection. This is a bug in an application. See dbus_connection_unref() documentation for details.\n%s",
+ connection->shareable ?
+ "Most likely, the application called unref() too many times and removed a reference belonging to libdbus, since this is a shared connection." :
+ "Most likely, the application was supposed to call dbus_connection_close(), since this is a private connection.");
+ return;
+ }
+#endif
+ _dbus_connection_last_unref (connection);
+ }
+}
+
+/*
+ * Note that the transport can disconnect itself (other end drops us)
+ * and in that case this function never runs. So this function must
+ * not do anything more than disconnect the transport and update the
+ * dispatch status.
+ *
+ * If the transport self-disconnects, then we assume someone will
+ * dispatch the connection to cause the dispatch status update.
+ */
+static void
+_dbus_connection_close_possibly_shared_and_unlock (DBusConnection *connection)
+{
+ DBusDispatchStatus status;
+
+ HAVE_LOCK_CHECK (connection);
+
+ _dbus_verbose ("Disconnecting %p\n", connection);
+
+ /* We need to ref because update_dispatch_status_and_unlock will unref
+ * the connection if it was shared and libdbus was the only remaining
+ * refcount holder.
+ */
+ _dbus_connection_ref_unlocked (connection);
+
+ _dbus_transport_disconnect (connection->transport);
+
+ /* This has the side effect of queuing the disconnect message link
+ * (unless we don't have enough memory, possibly, so don't assert it).
+ * After the disconnect message link is queued, dbus_bus_get/dbus_connection_open
+ * should never again return the newly-disconnected connection.
+ *
+ * However, we only unref the shared connection and exit_on_disconnect when
+ * the disconnect message reaches the head of the message queue,
+ * NOT when it's first queued.
+ */
+ status = _dbus_connection_get_dispatch_status_unlocked (connection);
+
+ /* This calls out to user code */
+ _dbus_connection_update_dispatch_status_and_unlock (connection, status);
+
+ /* Could also call out to user code */
+ dbus_connection_unref (connection);
+}
+
+/**
+ * Closes a private connection, so no further data can be sent or received.
+ * This disconnects the transport (such as a socket) underlying the
+ * connection.
+ *
+ * Attempts to send messages after closing a connection are safe, but will result in
+ * error replies generated locally in libdbus.
+ *
+ * This function does not affect the connection's reference count. It's
+ * safe to close a connection more than once; all calls after the
+ * first do nothing. It's impossible to "reopen" a connection, a
+ * new connection must be created. This function may result in a call
+ * to the DBusDispatchStatusFunction set with
+ * dbus_connection_set_dispatch_status_function(), as the disconnect
+ * message it generates needs to be dispatched.
+ *
+ * If a connection is dropped by the remote application, it will
+ * close itself.
+ *
+ * You must close a connection prior to releasing the last reference to
+ * the connection. If you dbus_connection_unref() for the last time
+ * without closing the connection, the results are undefined; it
+ * is a bug in your program and libdbus will try to print a warning.
+ *
+ * You may not close a shared connection. Connections created with
+ * dbus_connection_open() or dbus_bus_get() are shared.
+ * These connections are owned by libdbus, and applications should
+ * only unref them, never close them. Applications can know it is
+ * safe to unref these connections because libdbus will be holding a
+ * reference as long as the connection is open. Thus, either the
+ * connection is closed and it is OK to drop the last reference,
+ * or the connection is open and the app knows it does not have the
+ * last reference.
+ *
+ * Connections created with dbus_connection_open_private() or
+ * dbus_bus_get_private() are not kept track of or referenced by
+ * libdbus. The creator of these connections is responsible for
+ * calling dbus_connection_close() prior to releasing the last
+ * reference, if the connection is not already disconnected.
+ *
+ * @param connection the private (unshared) connection to close
+ */
+void
+dbus_connection_close (DBusConnection *connection)
+{
+ _dbus_return_if_fail (connection != NULL);
+ _dbus_return_if_fail (connection->generation == _dbus_current_generation);
+
+ CONNECTION_LOCK (connection);
+
+#ifndef DBUS_DISABLE_CHECKS
+ if (connection->shareable)
+ {
+ CONNECTION_UNLOCK (connection);
+
+ _dbus_warn_check_failed ("Applications must not close shared connections - see dbus_connection_close() docs. This is a bug in the application.");
+ return;
+ }
+#endif
+
+ _dbus_connection_close_possibly_shared_and_unlock (connection);
+}
+
+static dbus_bool_t
+_dbus_connection_get_is_connected_unlocked (DBusConnection *connection)
+{
+ HAVE_LOCK_CHECK (connection);
+ return _dbus_transport_get_is_connected (connection->transport);
+}
+
+/**
+ * Gets whether the connection is currently open. A connection may
+ * become disconnected when the remote application closes its end, or
+ * exits; a connection may also be disconnected with
+ * dbus_connection_close().
+ *
+ * There are not separate states for "closed" and "disconnected," the two
+ * terms are synonymous. This function should really be called
+ * get_is_open() but for historical reasons is not.
+ *
+ * @param connection the connection.
+ * @returns #TRUE if the connection is still alive.
+ */
+dbus_bool_t
+dbus_connection_get_is_connected (DBusConnection *connection)
+{
+ dbus_bool_t res;
+
+ _dbus_return_val_if_fail (connection != NULL, FALSE);
+
+ CONNECTION_LOCK (connection);
+ res = _dbus_connection_get_is_connected_unlocked (connection);
+ CONNECTION_UNLOCK (connection);
+
+ return res;
+}
+
+/**
+ * Gets whether the connection was authenticated. (Note that
+ * if the connection was authenticated then disconnected,
+ * this function still returns #TRUE)
+ *
+ * @param connection the connection
+ * @returns #TRUE if the connection was ever authenticated
+ */
+dbus_bool_t
+dbus_connection_get_is_authenticated (DBusConnection *connection)
+{
+ dbus_bool_t res;
+
+ _dbus_return_val_if_fail (connection != NULL, FALSE);
+
+ CONNECTION_LOCK (connection);
+ res = _dbus_transport_try_to_authenticate (connection->transport);
+ CONNECTION_UNLOCK (connection);
+
+ return res;
+}
+
+/**
+ * Gets whether the connection is not authenticated as a specific
+ * user. If the connection is not authenticated, this function
+ * returns #TRUE, and if it is authenticated but as an anonymous user,
+ * it returns #TRUE. If it is authenticated as a specific user, then
+ * this returns #FALSE. (Note that if the connection was authenticated
+ * as anonymous then disconnected, this function still returns #TRUE.)
+ *
+ * If the connection is not anonymous, you can use
+ * dbus_connection_get_unix_user() and
+ * dbus_connection_get_windows_user() to see who it's authorized as.
+ *
+ * If you want to prevent non-anonymous authorization, use
+ * dbus_server_set_auth_mechanisms() to remove the mechanisms that
+ * allow proving user identity (i.e. only allow the ANONYMOUS
+ * mechanism).
+ *
+ * @param connection the connection
+ * @returns #TRUE if not authenticated or authenticated as anonymous
+ */
+dbus_bool_t
+dbus_connection_get_is_anonymous (DBusConnection *connection)
+{
+ dbus_bool_t res;
+
+ _dbus_return_val_if_fail (connection != NULL, FALSE);
+
+ CONNECTION_LOCK (connection);
+ res = _dbus_transport_get_is_anonymous (connection->transport);
+ CONNECTION_UNLOCK (connection);
+
+ return res;
+}
+
+/**
+ * Gets the ID of the server address we are authenticated to, if this
+ * connection is on the client side. If the connection is on the
+ * server side, this will always return #NULL - use dbus_server_get_id()
+ * to get the ID of your own server, if you are the server side.
+ *
+ * If a client-side connection is not authenticated yet, the ID may be
+ * available if it was included in the server address, but may not be
+ * available. The only way to be sure the server ID is available
+ * is to wait for authentication to complete.
+ *
+ * In general, each mode of connecting to a given server will have
+ * its own ID. So for example, if the session bus daemon is listening
+ * on UNIX domain sockets and on TCP, then each of those modalities
+ * will have its own server ID.
+ *
+ * If you want an ID that identifies an entire session bus, look at
+ * dbus_bus_get_id() instead (which is just a convenience wrapper
+ * around the org.freedesktop.DBus.GetId method invoked on the bus).
+ *
+ * You can also get a machine ID; see dbus_try_get_local_machine_id() to
+ * get the machine you are on. There isn't a convenience wrapper, but
+ * you can invoke org.freedesktop.DBus.Peer.GetMachineId on any peer
+ * to get the machine ID on the other end.
+ *
+ * The D-Bus specification describes the server ID and other IDs in a
+ * bit more detail.
+ *
+ * @param connection the connection
+ * @returns the server ID or #NULL if no memory or the connection is server-side
+ */
+char*
+dbus_connection_get_server_id (DBusConnection *connection)
+{
+ char *id;
+
+ _dbus_return_val_if_fail (connection != NULL, NULL);
+
+ CONNECTION_LOCK (connection);
+ id = _dbus_strdup (_dbus_transport_get_server_id (connection->transport));
+ CONNECTION_UNLOCK (connection);
+
+ return id;
+}
+
+/**
+ * Tests whether a certain type can be send via the connection. This
+ * will always return TRUE for all types, with the exception of
+ * DBUS_TYPE_UNIX_FD. The function will return TRUE for
+ * DBUS_TYPE_UNIX_FD only on systems that know Unix file descriptors
+ * and can send them via the chosen transport and when the remote side
+ * supports this.
+ *
+ * This function can be used to do runtime checking for types that
+ * might be unknown to the specific D-Bus client implementation
+ * version, i.e. it will return FALSE for all types this
+ * implementation does not know, including invalid or reserved types.
+ *
+ * @param connection the connection
+ * @param type the type to check
+ * @returns TRUE if the type may be send via the connection
+ */
+dbus_bool_t
+dbus_connection_can_send_type(DBusConnection *connection,
+ int type)
+{
+ _dbus_return_val_if_fail (connection != NULL, FALSE);
+
+ if (!dbus_type_is_valid (type))
+ return FALSE;
+
+ if (type != DBUS_TYPE_UNIX_FD)
+ return TRUE;
+
+#ifdef HAVE_UNIX_FD_PASSING
+ {
+ dbus_bool_t b;
+
+ CONNECTION_LOCK(connection);
+ b = _dbus_transport_can_pass_unix_fd(connection->transport);
+ CONNECTION_UNLOCK(connection);
+
+ return b;
+ }
+#endif
+
+ return FALSE;
+}
+
+/**
+ * Set whether _exit() should be called when the connection receives a
+ * disconnect signal. The call to _exit() comes after any handlers for
+ * the disconnect signal run; handlers can cancel the exit by calling
+ * this function.
+ *
+ * By default, exit_on_disconnect is #FALSE; but for message bus
+ * connections returned from dbus_bus_get() it will be toggled on
+ * by default.
+ *
+ * @param connection the connection
+ * @param exit_on_disconnect #TRUE if _exit() should be called after a disconnect signal
+ */
+void
+dbus_connection_set_exit_on_disconnect (DBusConnection *connection,
+ dbus_bool_t exit_on_disconnect)
+{
+ _dbus_return_if_fail (connection != NULL);
+
+ CONNECTION_LOCK (connection);
+ connection->exit_on_disconnect = exit_on_disconnect != FALSE;
+ CONNECTION_UNLOCK (connection);
+}
+
+/**
+ * Preallocates resources needed to send a message, allowing the message
+ * to be sent without the possibility of memory allocation failure.
+ * Allows apps to create a future guarantee that they can send
+ * a message regardless of memory shortages.
+ *
+ * @param connection the connection we're preallocating for.
+ * @returns the preallocated resources, or #NULL
+ */
+DBusPreallocatedSend*
+dbus_connection_preallocate_send (DBusConnection *connection)
+{
+ DBusPreallocatedSend *preallocated;
+
+ _dbus_return_val_if_fail (connection != NULL, NULL);
+
+ CONNECTION_LOCK (connection);
+
+ preallocated =
+ _dbus_connection_preallocate_send_unlocked (connection);
+
+ CONNECTION_UNLOCK (connection);
+
+ return preallocated;
+}
+
+/**
+ * Frees preallocated message-sending resources from
+ * dbus_connection_preallocate_send(). Should only
+ * be called if the preallocated resources are not used
+ * to send a message.
+ *
+ * @param connection the connection
+ * @param preallocated the resources
+ */
+void
+dbus_connection_free_preallocated_send (DBusConnection *connection,
+ DBusPreallocatedSend *preallocated)
+{
+ _dbus_return_if_fail (connection != NULL);
+ _dbus_return_if_fail (preallocated != NULL);
+ _dbus_return_if_fail (connection == preallocated->connection);
+
+ _dbus_list_free_link (preallocated->queue_link);
+ _dbus_counter_unref (preallocated->counter_link->data);
+ _dbus_list_free_link (preallocated->counter_link);
+ dbus_free (preallocated);
+}
+
+/**
+ * Sends a message using preallocated resources. This function cannot fail.
+ * It works identically to dbus_connection_send() in other respects.
+ * Preallocated resources comes from dbus_connection_preallocate_send().
+ * This function "consumes" the preallocated resources, they need not
+ * be freed separately.
+ *
+ * @param connection the connection
+ * @param preallocated the preallocated resources
+ * @param message the message to send
+ * @param client_serial return location for client serial assigned to the message
+ */
+void
+dbus_connection_send_preallocated (DBusConnection *connection,
+ DBusPreallocatedSend *preallocated,
+ DBusMessage *message,
+ dbus_uint32_t *client_serial)
+{
+ _dbus_return_if_fail (connection != NULL);
+ _dbus_return_if_fail (preallocated != NULL);
+ _dbus_return_if_fail (message != NULL);
+ _dbus_return_if_fail (preallocated->connection == connection);
+ _dbus_return_if_fail (dbus_message_get_type (message) != DBUS_MESSAGE_TYPE_METHOD_CALL ||
+ dbus_message_get_member (message) != NULL);
+ _dbus_return_if_fail (dbus_message_get_type (message) != DBUS_MESSAGE_TYPE_SIGNAL ||
+ (dbus_message_get_interface (message) != NULL &&
+ dbus_message_get_member (message) != NULL));
+
+ CONNECTION_LOCK (connection);
+
+#ifdef HAVE_UNIX_FD_PASSING
+
+ if (!_dbus_transport_can_pass_unix_fd(connection->transport) &&
+ message->n_unix_fds > 0)
+ {
+ /* Refuse to send fds on a connection that cannot handle
+ them. Unfortunately we cannot return a proper error here, so
+ the best we can is just return. */
+ CONNECTION_UNLOCK (connection);
+ return;
+ }
+
+#endif
+
+ _dbus_connection_send_preallocated_and_unlock (connection,
+ preallocated,
+ message, client_serial);
+}
+
+static dbus_bool_t
+_dbus_connection_send_unlocked_no_update (DBusConnection *connection,
+ DBusMessage *message,
+ dbus_uint32_t *client_serial)
+{
+ DBusPreallocatedSend *preallocated;
+
+ _dbus_assert (connection != NULL);
+ _dbus_assert (message != NULL);
+
+ preallocated = _dbus_connection_preallocate_send_unlocked (connection);
+ if (preallocated == NULL)
+ return FALSE;
+
+ _dbus_connection_send_preallocated_unlocked_no_update (connection,
+ preallocated,
+ message,
+ client_serial);
+ return TRUE;
+}
+
+/**
+ * Adds a message to the outgoing message queue. Does not block to
+ * write the message to the network; that happens asynchronously. To
+ * force the message to be written, call dbus_connection_flush() however
+ * it is not necessary to call dbus_connection_flush() by hand; the
+ * message will be sent the next time the main loop is run.
+ * dbus_connection_flush() should only be used, for example, if
+ * the application was expected to exit before running the main loop.
+ *
+ * Because this only queues the message, the only reason it can
+ * fail is lack of memory. Even if the connection is disconnected,
+ * no error will be returned. If the function fails due to lack of memory,
+ * it returns #FALSE. The function will never fail for other reasons; even
+ * if the connection is disconnected, you can queue an outgoing message,
+ * though obviously it won't be sent.
+ *
+ * The message serial is used by the remote application to send a
+ * reply; see dbus_message_get_serial() or the D-Bus specification.
+ *
+ * dbus_message_unref() can be called as soon as this method returns
+ * as the message queue will hold its own ref until the message is sent.
+ *
+ * @param connection the connection.
+ * @param message the message to write.
+ * @param serial return location for message serial, or #NULL if you don't care
+ * @returns #TRUE on success.
+ */
+dbus_bool_t
+dbus_connection_send (DBusConnection *connection,
+ DBusMessage *message,
+ dbus_uint32_t *serial)
+{
+ _dbus_return_val_if_fail (connection != NULL, FALSE);
+ _dbus_return_val_if_fail (message != NULL, FALSE);
+
+ CONNECTION_LOCK (connection);
+
+#ifdef HAVE_UNIX_FD_PASSING
+
+ if (!_dbus_transport_can_pass_unix_fd(connection->transport) &&
+ message->n_unix_fds > 0)
+ {
+ /* Refuse to send fds on a connection that cannot handle
+ them. Unfortunately we cannot return a proper error here, so
+ the best we can is just return. */
+ CONNECTION_UNLOCK (connection);
+ return FALSE;
+ }
+
+#endif
+
+ return _dbus_connection_send_and_unlock (connection,
+ message,
+ serial);
+}
+
+static dbus_bool_t
+reply_handler_timeout (void *data)
+{
+ DBusConnection *connection;
+ DBusDispatchStatus status;
+ DBusPendingCall *pending = data;
+
+ connection = _dbus_pending_call_get_connection_and_lock (pending);
+ _dbus_connection_ref_unlocked (connection);
+
+ _dbus_pending_call_queue_timeout_error_unlocked (pending,
+ connection);
+ _dbus_connection_remove_timeout_unlocked (connection,
+ _dbus_pending_call_get_timeout_unlocked (pending));
+ _dbus_pending_call_set_timeout_added_unlocked (pending, FALSE);
+
+ _dbus_verbose ("middle\n");
+ status = _dbus_connection_get_dispatch_status_unlocked (connection);
+
+ /* Unlocks, and calls out to user code */
+ _dbus_connection_update_dispatch_status_and_unlock (connection, status);
+ dbus_connection_unref (connection);
+
+ return TRUE;
+}
+
+/**
+ * Queues a message to send, as with dbus_connection_send(),
+ * but also returns a #DBusPendingCall used to receive a reply to the
+ * message. If no reply is received in the given timeout_milliseconds,
+ * this function expires the pending reply and generates a synthetic
+ * error reply (generated in-process, not by the remote application)
+ * indicating that a timeout occurred.
+ *
+ * A #DBusPendingCall will see a reply message before any filters or
+ * registered object path handlers. See dbus_connection_dispatch() for
+ * details on when handlers are run.
+ *
+ * A #DBusPendingCall will always see exactly one reply message,
+ * unless it's cancelled with dbus_pending_call_cancel().
+ *
+ * If #NULL is passed for the pending_return, the #DBusPendingCall
+ * will still be generated internally, and used to track
+ * the message reply timeout. This means a timeout error will
+ * occur if no reply arrives, unlike with dbus_connection_send().
+ *
+ * If -1 is passed for the timeout, a sane default timeout is used. -1
+ * is typically the best value for the timeout for this reason, unless
+ * you want a very short or very long timeout. If #DBUS_TIMEOUT_INFINITE is
+ * passed for the timeout, no timeout will be set and the call will block
+ * forever.
+ *
+ * @warning if the connection is disconnected or you try to send Unix
+ * file descriptors on a connection that does not support them, the
+ * #DBusPendingCall will be set to #NULL, so be careful with this.
+ *
+ * @param connection the connection
+ * @param message the message to send
+ * @param pending_return return location for a #DBusPendingCall
+ * object, or #NULL if connection is disconnected or when you try to
+ * send Unix file descriptors on a connection that does not support
+ * them.
+ * @param timeout_milliseconds timeout in milliseconds, -1 (or
+ * #DBUS_TIMEOUT_USE_DEFAULT) for default or #DBUS_TIMEOUT_INFINITE for no
+ * timeout
+ * @returns #FALSE if no memory, #TRUE otherwise.
+ *
+ */
+dbus_bool_t
+dbus_connection_send_with_reply (DBusConnection *connection,
+ DBusMessage *message,
+ DBusPendingCall **pending_return,
+ int timeout_milliseconds)
+{
+ DBusPendingCall *pending;
+ dbus_int32_t serial = -1;
+ DBusDispatchStatus status;
+
+ _dbus_return_val_if_fail (connection != NULL, FALSE);
+ _dbus_return_val_if_fail (message != NULL, FALSE);
+ _dbus_return_val_if_fail (timeout_milliseconds >= 0 || timeout_milliseconds == -1, FALSE);
+
+ if (pending_return)
+ *pending_return = NULL;
+
+ CONNECTION_LOCK (connection);
+
+#ifdef HAVE_UNIX_FD_PASSING
+
+ if (!_dbus_transport_can_pass_unix_fd(connection->transport) &&
+ message->n_unix_fds > 0)
+ {
+ /* Refuse to send fds on a connection that cannot handle
+ them. Unfortunately we cannot return a proper error here, so
+ the best we can do is return TRUE but leave *pending_return
+ as NULL. */
+ CONNECTION_UNLOCK (connection);
+ return TRUE;
+ }
+
+#endif
+
+ if (!_dbus_connection_get_is_connected_unlocked (connection))
+ {
+ CONNECTION_UNLOCK (connection);
+
+ return TRUE;
+ }
+
+ pending = _dbus_pending_call_new_unlocked (connection,
+ timeout_milliseconds,
+ reply_handler_timeout);
+
+ if (pending == NULL)
+ {
+ CONNECTION_UNLOCK (connection);
+ return FALSE;
+ }
+
+ /* Assign a serial to the message */
+ serial = dbus_message_get_serial (message);
+ if (serial == 0)
+ {
+ serial = _dbus_connection_get_next_client_serial (connection);
+ dbus_message_set_serial (message, serial);
+ }
+
+ if (!_dbus_pending_call_set_timeout_error_unlocked (pending, message, serial))
+ goto error;
+
+ /* Insert the serial in the pending replies hash;
+ * hash takes a refcount on DBusPendingCall.
+ * Also, add the timeout.
+ */
+ if (!_dbus_connection_attach_pending_call_unlocked (connection,
+ pending))
+ goto error;
+
+ if (!_dbus_connection_send_unlocked_no_update (connection, message, NULL))
+ {
+ _dbus_connection_detach_pending_call_and_unlock (connection,
+ pending);
+ goto error_unlocked;
+ }
+
+ if (pending_return)
+ *pending_return = pending; /* hand off refcount */
+ else
+ {
+ _dbus_connection_detach_pending_call_unlocked (connection, pending);
+ /* we still have a ref to the pending call in this case, we unref
+ * after unlocking, below
+ */
+ }
+
+ status = _dbus_connection_get_dispatch_status_unlocked (connection);
+
+ /* this calls out to user code */
+ _dbus_connection_update_dispatch_status_and_unlock (connection, status);
+
+ if (pending_return == NULL)
+ dbus_pending_call_unref (pending);
+
+ return TRUE;
+
+ error:
+ CONNECTION_UNLOCK (connection);
+ error_unlocked:
+ dbus_pending_call_unref (pending);
+ return FALSE;
+}
+
+/**
+ * Sends a message and blocks a certain time period while waiting for
+ * a reply. This function does not reenter the main loop,
+ * i.e. messages other than the reply are queued up but not
+ * processed. This function is used to invoke method calls on a
+ * remote object.
+ *
+ * If a normal reply is received, it is returned, and removed from the
+ * incoming message queue. If it is not received, #NULL is returned
+ * and the error is set to #DBUS_ERROR_NO_REPLY. If an error reply is
+ * received, it is converted to a #DBusError and returned as an error,
+ * then the reply message is deleted and #NULL is returned. If
+ * something else goes wrong, result is set to whatever is
+ * appropriate, such as #DBUS_ERROR_NO_MEMORY or
+ * #DBUS_ERROR_DISCONNECTED.
+ *
+ * @warning While this function blocks the calling thread will not be
+ * processing the incoming message queue. This means you can end up
+ * deadlocked if the application you're talking to needs you to reply
+ * to a method. To solve this, either avoid the situation, block in a
+ * separate thread from the main connection-dispatching thread, or use
+ * dbus_pending_call_set_notify() to avoid blocking.
+ *
+ * @param connection the connection
+ * @param message the message to send
+ * @param timeout_milliseconds timeout in milliseconds, -1 (or
+ * #DBUS_TIMEOUT_USE_DEFAULT) for default or #DBUS_TIMEOUT_INFINITE for no
+ * timeout
+ * @param error return location for error message
+ * @returns the message that is the reply or #NULL with an error code if the
+ * function fails.
+ */
+DBusMessage*
+dbus_connection_send_with_reply_and_block (DBusConnection *connection,
+ DBusMessage *message,
+ int timeout_milliseconds,
+ DBusError *error)
+{
+ DBusMessage *reply;
+ DBusPendingCall *pending;
+
+ _dbus_return_val_if_fail (connection != NULL, NULL);
+ _dbus_return_val_if_fail (message != NULL, NULL);
+ _dbus_return_val_if_fail (timeout_milliseconds >= 0 || timeout_milliseconds == -1, NULL);
+ _dbus_return_val_if_error_is_set (error, NULL);
+
+#ifdef HAVE_UNIX_FD_PASSING
+
+ CONNECTION_LOCK (connection);
+ if (!_dbus_transport_can_pass_unix_fd(connection->transport) &&
+ message->n_unix_fds > 0)
+ {
+ CONNECTION_UNLOCK (connection);
+ dbus_set_error(error, DBUS_ERROR_FAILED, "Cannot send file descriptors on this connection.");
+ return NULL;
+ }
+ CONNECTION_UNLOCK (connection);
+
+#endif
+
+ if (!dbus_connection_send_with_reply (connection, message,
+ &pending, timeout_milliseconds))
+ {
+ _DBUS_SET_OOM (error);
+ return NULL;
+ }
+
+ if (pending == NULL)
+ {
+ dbus_set_error (error, DBUS_ERROR_DISCONNECTED, "Connection is closed");
+ return NULL;
+ }
+
+ dbus_pending_call_block (pending);
+
+ reply = dbus_pending_call_steal_reply (pending);
+ dbus_pending_call_unref (pending);
+
+ /* call_complete_and_unlock() called from pending_call_block() should
+ * always fill this in.
+ */
+ _dbus_assert (reply != NULL);
+
+ if (dbus_set_error_from_message (error, reply))
+ {
+ dbus_message_unref (reply);
+ return NULL;
+ }
+ else
+ return reply;
+}
+
+/**
+ * Blocks until the outgoing message queue is empty.
+ * Assumes connection lock already held.
+ *
+ * If you call this, you MUST call update_dispatch_status afterword...
+ *
+ * @param connection the connection.
+ */
+static DBusDispatchStatus
+_dbus_connection_flush_unlocked (DBusConnection *connection)
+{
+ /* We have to specify DBUS_ITERATION_DO_READING here because
+ * otherwise we could have two apps deadlock if they are both doing
+ * a flush(), and the kernel buffers fill up. This could change the
+ * dispatch status.
+ */
+ DBusDispatchStatus status;
+
+ HAVE_LOCK_CHECK (connection);
+
+ while (connection->n_outgoing > 0 &&
+ _dbus_connection_get_is_connected_unlocked (connection))
+ {
+ _dbus_verbose ("doing iteration in\n");
+ HAVE_LOCK_CHECK (connection);
+ _dbus_connection_do_iteration_unlocked (connection,
+ NULL,
+ DBUS_ITERATION_DO_READING |
+ DBUS_ITERATION_DO_WRITING |
+ DBUS_ITERATION_BLOCK,
+ -1);
+ }
+
+ HAVE_LOCK_CHECK (connection);
+ _dbus_verbose ("middle\n");
+ status = _dbus_connection_get_dispatch_status_unlocked (connection);
+
+ HAVE_LOCK_CHECK (connection);
+ return status;
+}
+
+/**
+ * Blocks until the outgoing message queue is empty.
+ *
+ * @param connection the connection.
+ */
+void
+dbus_connection_flush (DBusConnection *connection)
+{
+ /* We have to specify DBUS_ITERATION_DO_READING here because
+ * otherwise we could have two apps deadlock if they are both doing
+ * a flush(), and the kernel buffers fill up. This could change the
+ * dispatch status.
+ */
+ DBusDispatchStatus status;
+
+ _dbus_return_if_fail (connection != NULL);
+
+ CONNECTION_LOCK (connection);
+
+ status = _dbus_connection_flush_unlocked (connection);
+
+ HAVE_LOCK_CHECK (connection);
+ /* Unlocks and calls out to user code */
+ _dbus_connection_update_dispatch_status_and_unlock (connection, status);
+
+ _dbus_verbose ("end\n");
+}
+
+/**
+ * This function implements dbus_connection_read_write_dispatch() and
+ * dbus_connection_read_write() (they pass a different value for the
+ * dispatch parameter).
+ *
+ * @param connection the connection
+ * @param timeout_milliseconds max time to block or -1 for infinite
+ * @param dispatch dispatch new messages or leave them on the incoming queue
+ * @returns #TRUE if the disconnect message has not been processed
+ */
+static dbus_bool_t
+_dbus_connection_read_write_dispatch (DBusConnection *connection,
+ int timeout_milliseconds,
+ dbus_bool_t dispatch)
+{
+ DBusDispatchStatus dstatus;
+ dbus_bool_t progress_possible;
+
+ /* Need to grab a ref here in case we're a private connection and
+ * the user drops the last ref in a handler we call; see bug
+ * https://bugs.freedesktop.org/show_bug.cgi?id=15635
+ */
+ dbus_connection_ref (connection);
+ dstatus = dbus_connection_get_dispatch_status (connection);
+
+ if (dispatch && dstatus == DBUS_DISPATCH_DATA_REMAINS)
+ {
+ _dbus_verbose ("doing dispatch\n");
+ dbus_connection_dispatch (connection);
+ CONNECTION_LOCK (connection);
+ }
+ else if (dstatus == DBUS_DISPATCH_NEED_MEMORY)
+ {
+ _dbus_verbose ("pausing for memory\n");
+ _dbus_memory_pause_based_on_timeout (timeout_milliseconds);
+ CONNECTION_LOCK (connection);
+ }
+ else
+ {
+ CONNECTION_LOCK (connection);
+ if (_dbus_connection_get_is_connected_unlocked (connection))
+ {
+ _dbus_verbose ("doing iteration\n");
+ _dbus_connection_do_iteration_unlocked (connection,
+ NULL,
+ DBUS_ITERATION_DO_READING |
+ DBUS_ITERATION_DO_WRITING |
+ DBUS_ITERATION_BLOCK,
+ timeout_milliseconds);
+ }
+ }
+
+ HAVE_LOCK_CHECK (connection);
+ /* If we can dispatch, we can make progress until the Disconnected message
+ * has been processed; if we can only read/write, we can make progress
+ * as long as the transport is open.
+ */
+ if (dispatch)
+ progress_possible = connection->n_incoming != 0 ||
+ connection->disconnect_message_link != NULL;
+ else
+ progress_possible = _dbus_connection_get_is_connected_unlocked (connection);
+
+ CONNECTION_UNLOCK (connection);
+
+ dbus_connection_unref (connection);
+
+ return progress_possible; /* TRUE if we can make more progress */
+}
+
+
+/**
+ * This function is intended for use with applications that don't want
+ * to write a main loop and deal with #DBusWatch and #DBusTimeout. An
+ * example usage would be:
+ *
+ * @code
+ * while (dbus_connection_read_write_dispatch (connection, -1))
+ * ; // empty loop body
+ * @endcode
+ *
+ * In this usage you would normally have set up a filter function to look
+ * at each message as it is dispatched. The loop terminates when the last
+ * message from the connection (the disconnected signal) is processed.
+ *
+ * If there are messages to dispatch, this function will
+ * dbus_connection_dispatch() once, and return. If there are no
+ * messages to dispatch, this function will block until it can read or
+ * write, then read or write, then return.
+ *
+ * The way to think of this function is that it either makes some sort
+ * of progress, or it blocks. Note that, while it is blocked on I/O, it
+ * cannot be interrupted (even by other threads), which makes this function
+ * unsuitable for applications that do more than just react to received
+ * messages.
+ *
+ * The return value indicates whether the disconnect message has been
+ * processed, NOT whether the connection is connected. This is
+ * important because even after disconnecting, you want to process any
+ * messages you received prior to the disconnect.
+ *
+ * @param connection the connection
+ * @param timeout_milliseconds max time to block or -1 for infinite
+ * @returns #TRUE if the disconnect message has not been processed
+ */
+dbus_bool_t
+dbus_connection_read_write_dispatch (DBusConnection *connection,
+ int timeout_milliseconds)
+{
+ _dbus_return_val_if_fail (connection != NULL, FALSE);
+ _dbus_return_val_if_fail (timeout_milliseconds >= 0 || timeout_milliseconds == -1, FALSE);
+ return _dbus_connection_read_write_dispatch(connection, timeout_milliseconds, TRUE);
+}
+
+/**
+ * This function is intended for use with applications that don't want to
+ * write a main loop and deal with #DBusWatch and #DBusTimeout. See also
+ * dbus_connection_read_write_dispatch().
+ *
+ * As long as the connection is open, this function will block until it can
+ * read or write, then read or write, then return #TRUE.
+ *
+ * If the connection is closed, the function returns #FALSE.
+ *
+ * The return value indicates whether reading or writing is still
+ * possible, i.e. whether the connection is connected.
+ *
+ * Note that even after disconnection, messages may remain in the
+ * incoming queue that need to be
+ * processed. dbus_connection_read_write_dispatch() dispatches
+ * incoming messages for you; with dbus_connection_read_write() you
+ * have to arrange to drain the incoming queue yourself.
+ *
+ * @param connection the connection
+ * @param timeout_milliseconds max time to block or -1 for infinite
+ * @returns #TRUE if still connected
+ */
+dbus_bool_t
+dbus_connection_read_write (DBusConnection *connection,
+ int timeout_milliseconds)
+{
+ _dbus_return_val_if_fail (connection != NULL, FALSE);
+ _dbus_return_val_if_fail (timeout_milliseconds >= 0 || timeout_milliseconds == -1, FALSE);
+ return _dbus_connection_read_write_dispatch(connection, timeout_milliseconds, FALSE);
+}
+
+/* We need to call this anytime we pop the head of the queue, and then
+ * update_dispatch_status_and_unlock needs to be called afterward
+ * which will "process" the disconnected message and set
+ * disconnected_message_processed.
+ */
+static void
+check_disconnected_message_arrived_unlocked (DBusConnection *connection,
+ DBusMessage *head_of_queue)
+{
+ HAVE_LOCK_CHECK (connection);
+
+ /* checking that the link is NULL is an optimization to avoid the is_signal call */
+ if (connection->disconnect_message_link == NULL &&
+ dbus_message_is_signal (head_of_queue,
+ DBUS_INTERFACE_LOCAL,
+ "Disconnected"))
+ {
+ connection->disconnected_message_arrived = TRUE;
+ }
+}
+
+/**
+ * Returns the first-received message from the incoming message queue,
+ * leaving it in the queue. If the queue is empty, returns #NULL.
+ *
+ * The caller does not own a reference to the returned message, and
+ * must either return it using dbus_connection_return_message() or
+ * keep it after calling dbus_connection_steal_borrowed_message(). No
+ * one can get at the message while its borrowed, so return it as
+ * quickly as possible and don't keep a reference to it after
+ * returning it. If you need to keep the message, make a copy of it.
+ *
+ * dbus_connection_dispatch() will block if called while a borrowed
+ * message is outstanding; only one piece of code can be playing with
+ * the incoming queue at a time. This function will block if called
+ * during a dbus_connection_dispatch().
+ *
+ * @param connection the connection.
+ * @returns next message in the incoming queue.
+ */
+DBusMessage*
+dbus_connection_borrow_message (DBusConnection *connection)
+{
+ DBusDispatchStatus status;
+ DBusMessage *message;
+
+ _dbus_return_val_if_fail (connection != NULL, NULL);
+
+ _dbus_verbose ("start\n");
+
+ /* this is called for the side effect that it queues
+ * up any messages from the transport
+ */
+ status = dbus_connection_get_dispatch_status (connection);
+ if (status != DBUS_DISPATCH_DATA_REMAINS)
+ return NULL;
+
+ CONNECTION_LOCK (connection);
+
+ _dbus_connection_acquire_dispatch (connection);
+
+ /* While a message is outstanding, the dispatch lock is held */
+ _dbus_assert (connection->message_borrowed == NULL);
+
+ connection->message_borrowed = _dbus_list_get_first (&connection->incoming_messages);
+
+ message = connection->message_borrowed;
+
+ check_disconnected_message_arrived_unlocked (connection, message);
+
+ /* Note that we KEEP the dispatch lock until the message is returned */
+ if (message == NULL)
+ _dbus_connection_release_dispatch (connection);
+
+ CONNECTION_UNLOCK (connection);
+
+ _dbus_message_trace_ref (message, -1, -1, "dbus_connection_borrow_message");
+
+ /* We don't update dispatch status until it's returned or stolen */
+
+ return message;
+}
+
+/**
+ * Used to return a message after peeking at it using
+ * dbus_connection_borrow_message(). Only called if
+ * message from dbus_connection_borrow_message() was non-#NULL.
+ *
+ * @param connection the connection
+ * @param message the message from dbus_connection_borrow_message()
+ */
+void
+dbus_connection_return_message (DBusConnection *connection,
+ DBusMessage *message)
+{
+ DBusDispatchStatus status;
+
+ _dbus_return_if_fail (connection != NULL);
+ _dbus_return_if_fail (message != NULL);
+ _dbus_return_if_fail (message == connection->message_borrowed);
+ _dbus_return_if_fail (connection->dispatch_acquired);
+
+ CONNECTION_LOCK (connection);
+
+ _dbus_assert (message == connection->message_borrowed);
+
+ connection->message_borrowed = NULL;
+
+ _dbus_connection_release_dispatch (connection);
+
+ status = _dbus_connection_get_dispatch_status_unlocked (connection);
+ _dbus_connection_update_dispatch_status_and_unlock (connection, status);
+
+ _dbus_message_trace_ref (message, -1, -1, "dbus_connection_return_message");
+}
+
+/**
+ * Used to keep a message after peeking at it using
+ * dbus_connection_borrow_message(). Before using this function, see
+ * the caveats/warnings in the documentation for
+ * dbus_connection_pop_message().
+ *
+ * @param connection the connection
+ * @param message the message from dbus_connection_borrow_message()
+ */
+void
+dbus_connection_steal_borrowed_message (DBusConnection *connection,
+ DBusMessage *message)
+{
+ DBusMessage *pop_message;
+ DBusDispatchStatus status;
+
+ _dbus_return_if_fail (connection != NULL);
+ _dbus_return_if_fail (message != NULL);
+ _dbus_return_if_fail (message == connection->message_borrowed);
+ _dbus_return_if_fail (connection->dispatch_acquired);
+
+ CONNECTION_LOCK (connection);
+
+ _dbus_assert (message == connection->message_borrowed);
+
+ pop_message = _dbus_list_pop_first (&connection->incoming_messages);
+ _dbus_assert (message == pop_message);
+ (void) pop_message; /* unused unless asserting */
+
+ connection->n_incoming -= 1;
+
+ _dbus_verbose ("Incoming message %p stolen from queue, %d incoming\n",
+ message, connection->n_incoming);
+
+ connection->message_borrowed = NULL;
+
+ _dbus_connection_release_dispatch (connection);
+
+ status = _dbus_connection_get_dispatch_status_unlocked (connection);
+ _dbus_connection_update_dispatch_status_and_unlock (connection, status);
+ _dbus_message_trace_ref (message, -1, -1,
+ "dbus_connection_steal_borrowed_message");
+}
+
+/* See dbus_connection_pop_message, but requires the caller to own
+ * the lock before calling. May drop the lock while running.
+ */
+static DBusList*
+_dbus_connection_pop_message_link_unlocked (DBusConnection *connection)
+{
+ HAVE_LOCK_CHECK (connection);
+
+ _dbus_assert (connection->message_borrowed == NULL);
+
+ if (connection->n_incoming > 0)
+ {
+ DBusList *link;
+
+ link = _dbus_list_pop_first_link (&connection->incoming_messages);
+ connection->n_incoming -= 1;
+
+ _dbus_verbose ("Message %p (%s %s %s %s sig:'%s' serial:%u) removed from incoming queue %p, %d incoming\n",
+ link->data,
+ dbus_message_type_to_string (dbus_message_get_type (link->data)),
+ dbus_message_get_path (link->data) ?
+ dbus_message_get_path (link->data) :
+ "no path",
+ dbus_message_get_interface (link->data) ?
+ dbus_message_get_interface (link->data) :
+ "no interface",
+ dbus_message_get_member (link->data) ?
+ dbus_message_get_member (link->data) :
+ "no member",
+ dbus_message_get_signature (link->data),
+ dbus_message_get_serial (link->data),
+ connection, connection->n_incoming);
+
+ _dbus_message_trace_ref (link->data, -1, -1,
+ "_dbus_connection_pop_message_link_unlocked");
+
+ check_disconnected_message_arrived_unlocked (connection, link->data);
+
+ return link;
+ }
+ else
+ return NULL;
+}
+
+/* See dbus_connection_pop_message, but requires the caller to own
+ * the lock before calling. May drop the lock while running.
+ */
+static DBusMessage*
+_dbus_connection_pop_message_unlocked (DBusConnection *connection)
+{
+ DBusList *link;
+
+ HAVE_LOCK_CHECK (connection);
+
+ link = _dbus_connection_pop_message_link_unlocked (connection);
+
+ if (link != NULL)
+ {
+ DBusMessage *message;
+
+ message = link->data;
+
+ _dbus_list_free_link (link);
+
+ return message;
+ }
+ else
+ return NULL;
+}
+
+static void
+_dbus_connection_putback_message_link_unlocked (DBusConnection *connection,
+ DBusList *message_link)
+{
+ HAVE_LOCK_CHECK (connection);
+
+ _dbus_assert (message_link != NULL);
+ /* You can't borrow a message while a link is outstanding */
+ _dbus_assert (connection->message_borrowed == NULL);
+ /* We had to have the dispatch lock across the pop/putback */
+ _dbus_assert (connection->dispatch_acquired);
+
+ _dbus_list_prepend_link (&connection->incoming_messages,
+ message_link);
+ connection->n_incoming += 1;
+
+ _dbus_verbose ("Message %p (%s %s %s '%s') put back into queue %p, %d incoming\n",
+ message_link->data,
+ dbus_message_type_to_string (dbus_message_get_type (message_link->data)),
+ dbus_message_get_interface (message_link->data) ?
+ dbus_message_get_interface (message_link->data) :
+ "no interface",
+ dbus_message_get_member (message_link->data) ?
+ dbus_message_get_member (message_link->data) :
+ "no member",
+ dbus_message_get_signature (message_link->data),
+ connection, connection->n_incoming);
+
+ _dbus_message_trace_ref (message_link->data, -1, -1,
+ "_dbus_connection_putback_message_link_unlocked");
+}
+
+/**
+ * Returns the first-received message from the incoming message queue,
+ * removing it from the queue. The caller owns a reference to the
+ * returned message. If the queue is empty, returns #NULL.
+ *
+ * This function bypasses any message handlers that are registered,
+ * and so using it is usually wrong. Instead, let the main loop invoke
+ * dbus_connection_dispatch(). Popping messages manually is only
+ * useful in very simple programs that don't share a #DBusConnection
+ * with any libraries or other modules.
+ *
+ * There is a lock that covers all ways of accessing the incoming message
+ * queue, so dbus_connection_dispatch(), dbus_connection_pop_message(),
+ * dbus_connection_borrow_message(), etc. will all block while one of the others
+ * in the group is running.
+ *
+ * @param connection the connection.
+ * @returns next message in the incoming queue.
+ */
+DBusMessage*
+dbus_connection_pop_message (DBusConnection *connection)
+{
+ DBusMessage *message;
+ DBusDispatchStatus status;
+
+ _dbus_verbose ("start\n");
+
+ /* this is called for the side effect that it queues
+ * up any messages from the transport
+ */
+ status = dbus_connection_get_dispatch_status (connection);
+ if (status != DBUS_DISPATCH_DATA_REMAINS)
+ return NULL;
+
+ CONNECTION_LOCK (connection);
+ _dbus_connection_acquire_dispatch (connection);
+ HAVE_LOCK_CHECK (connection);
+
+ message = _dbus_connection_pop_message_unlocked (connection);
+
+ _dbus_verbose ("Returning popped message %p\n", message);
+
+ _dbus_connection_release_dispatch (connection);
+
+ status = _dbus_connection_get_dispatch_status_unlocked (connection);
+ _dbus_connection_update_dispatch_status_and_unlock (connection, status);
+
+ return message;
+}
+
+/**
+ * Acquire the dispatcher. This is a separate lock so the main
+ * connection lock can be dropped to call out to application dispatch
+ * handlers.
+ *
+ * @param connection the connection.
+ */
+static void
+_dbus_connection_acquire_dispatch (DBusConnection *connection)
+{
+ HAVE_LOCK_CHECK (connection);
+
+ _dbus_connection_ref_unlocked (connection);
+ CONNECTION_UNLOCK (connection);
+
+ _dbus_verbose ("locking dispatch_mutex\n");
+ _dbus_cmutex_lock (connection->dispatch_mutex);
+
+ while (connection->dispatch_acquired)
+ {
+ _dbus_verbose ("waiting for dispatch to be acquirable\n");
+ _dbus_condvar_wait (connection->dispatch_cond,
+ connection->dispatch_mutex);
+ }
+
+ _dbus_assert (!connection->dispatch_acquired);
+
+ connection->dispatch_acquired = TRUE;
+
+ _dbus_verbose ("unlocking dispatch_mutex\n");
+ _dbus_cmutex_unlock (connection->dispatch_mutex);
+
+ CONNECTION_LOCK (connection);
+ _dbus_connection_unref_unlocked (connection);
+}
+
+/**
+ * Release the dispatcher when you're done with it. Only call
+ * after you've acquired the dispatcher. Wakes up at most one
+ * thread currently waiting to acquire the dispatcher.
+ *
+ * @param connection the connection.
+ */
+static void
+_dbus_connection_release_dispatch (DBusConnection *connection)
+{
+ HAVE_LOCK_CHECK (connection);
+
+ _dbus_verbose ("locking dispatch_mutex\n");
+ _dbus_cmutex_lock (connection->dispatch_mutex);
+
+ _dbus_assert (connection->dispatch_acquired);
+
+ connection->dispatch_acquired = FALSE;
+ _dbus_condvar_wake_one (connection->dispatch_cond);
+
+ _dbus_verbose ("unlocking dispatch_mutex\n");
+ _dbus_cmutex_unlock (connection->dispatch_mutex);
+}
+
+static void
+_dbus_connection_failed_pop (DBusConnection *connection,
+ DBusList *message_link)
+{
+ _dbus_list_prepend_link (&connection->incoming_messages,
+ message_link);
+ connection->n_incoming += 1;
+}
+
+/* Note this may be called multiple times since we don't track whether we already did it */
+static void
+notify_disconnected_unlocked (DBusConnection *connection)
+{
+ HAVE_LOCK_CHECK (connection);
+
+ /* Set the weakref in dbus-bus.c to NULL, so nobody will get a disconnected
+ * connection from dbus_bus_get(). We make the same guarantee for
+ * dbus_connection_open() but in a different way since we don't want to
+ * unref right here; we instead check for connectedness before returning
+ * the connection from the hash.
+ */
+ _dbus_bus_notify_shared_connection_disconnected_unlocked (connection);
+
+ /* Dump the outgoing queue, we aren't going to be able to
+ * send it now, and we'd like accessors like
+ * dbus_connection_get_outgoing_size() to be accurate.
+ */
+ if (connection->n_outgoing > 0)
+ {
+ DBusList *link;
+
+ _dbus_verbose ("Dropping %d outgoing messages since we're disconnected\n",
+ connection->n_outgoing);
+
+ while ((link = _dbus_list_get_last_link (&connection->outgoing_messages)))
+ {
+ _dbus_connection_message_sent_unlocked (connection, link->data);
+ }
+ }
+}
+
+/* Note this may be called multiple times since we don't track whether we already did it */
+static DBusDispatchStatus
+notify_disconnected_and_dispatch_complete_unlocked (DBusConnection *connection)
+{
+ HAVE_LOCK_CHECK (connection);
+
+ if (connection->disconnect_message_link != NULL)
+ {
+ _dbus_verbose ("Sending disconnect message\n");
+
+ /* If we have pending calls, queue their timeouts - we want the Disconnected
+ * to be the last message, after these timeouts.
+ */
+ connection_timeout_and_complete_all_pending_calls_unlocked (connection);
+
+ /* We haven't sent the disconnect message already,
+ * and all real messages have been queued up.
+ */
+ _dbus_connection_queue_synthesized_message_link (connection,
+ connection->disconnect_message_link);
+ connection->disconnect_message_link = NULL;
+
+ return DBUS_DISPATCH_DATA_REMAINS;
+ }
+
+ return DBUS_DISPATCH_COMPLETE;
+}
+
+static DBusDispatchStatus
+_dbus_connection_get_dispatch_status_unlocked (DBusConnection *connection)
+{
+ HAVE_LOCK_CHECK (connection);
+
+ if (connection->n_incoming > 0)
+ return DBUS_DISPATCH_DATA_REMAINS;
+ else if (!_dbus_transport_queue_messages (connection->transport))
+ return DBUS_DISPATCH_NEED_MEMORY;
+ else
+ {
+ DBusDispatchStatus status;
+ dbus_bool_t is_connected;
+
+ status = _dbus_transport_get_dispatch_status (connection->transport);
+ is_connected = _dbus_transport_get_is_connected (connection->transport);
+
+ _dbus_verbose ("dispatch status = %s is_connected = %d\n",
+ DISPATCH_STATUS_NAME (status), is_connected);
+
+ if (!is_connected)
+ {
+ /* It's possible this would be better done by having an explicit
+ * notification from _dbus_transport_disconnect() that would
+ * synchronously do this, instead of waiting for the next dispatch
+ * status check. However, probably not good to change until it causes
+ * a problem.
+ */
+ notify_disconnected_unlocked (connection);
+
+ /* I'm not sure this is needed; the idea is that we want to
+ * queue the Disconnected only after we've read all the
+ * messages, but if we're disconnected maybe we are guaranteed
+ * to have read them all ?
+ */
+ if (status == DBUS_DISPATCH_COMPLETE)
+ status = notify_disconnected_and_dispatch_complete_unlocked (connection);
+ }
+
+ if (status != DBUS_DISPATCH_COMPLETE)
+ return status;
+ else if (connection->n_incoming > 0)
+ return DBUS_DISPATCH_DATA_REMAINS;
+ else
+ return DBUS_DISPATCH_COMPLETE;
+ }
+}
+
+static void
+_dbus_connection_update_dispatch_status_and_unlock (DBusConnection *connection,
+ DBusDispatchStatus new_status)
+{
+ dbus_bool_t changed;
+ DBusDispatchStatusFunction function;
+ void *data;
+
+ HAVE_LOCK_CHECK (connection);
+
+ _dbus_connection_ref_unlocked (connection);
+
+ changed = new_status != connection->last_dispatch_status;
+
+ connection->last_dispatch_status = new_status;
+
+ function = connection->dispatch_status_function;
+ data = connection->dispatch_status_data;
+
+ if (connection->disconnected_message_arrived &&
+ !connection->disconnected_message_processed)
+ {
+ connection->disconnected_message_processed = TRUE;
+
+ /* this does an unref, but we have a ref
+ * so we should not run the finalizer here
+ * inside the lock.
+ */
+ connection_forget_shared_unlocked (connection);
+
+ if (connection->exit_on_disconnect)
+ {
+ CONNECTION_UNLOCK (connection);
+
+ _dbus_verbose ("Exiting on Disconnected signal\n");
+ _dbus_exit (1);
+ _dbus_assert_not_reached ("Call to exit() returned");
+ }
+ }
+
+ /* We drop the lock */
+ CONNECTION_UNLOCK (connection);
+
+ if (changed && function)
+ {
+ _dbus_verbose ("Notifying of change to dispatch status of %p now %d (%s)\n",
+ connection, new_status,
+ DISPATCH_STATUS_NAME (new_status));
+ (* function) (connection, new_status, data);
+ }
+
+ dbus_connection_unref (connection);
+}
+
+/**
+ * Gets the current state of the incoming message queue.
+ * #DBUS_DISPATCH_DATA_REMAINS indicates that the message queue
+ * may contain messages. #DBUS_DISPATCH_COMPLETE indicates that the
+ * incoming queue is empty. #DBUS_DISPATCH_NEED_MEMORY indicates that
+ * there could be data, but we can't know for sure without more
+ * memory.
+ *
+ * To process the incoming message queue, use dbus_connection_dispatch()
+ * or (in rare cases) dbus_connection_pop_message().
+ *
+ * Note, #DBUS_DISPATCH_DATA_REMAINS really means that either we
+ * have messages in the queue, or we have raw bytes buffered up
+ * that need to be parsed. When these bytes are parsed, they
+ * may not add up to an entire message. Thus, it's possible
+ * to see a status of #DBUS_DISPATCH_DATA_REMAINS but not
+ * have a message yet.
+ *
+ * In particular this happens on initial connection, because all sorts
+ * of authentication protocol stuff has to be parsed before the
+ * first message arrives.
+ *
+ * @param connection the connection.
+ * @returns current dispatch status
+ */
+DBusDispatchStatus
+dbus_connection_get_dispatch_status (DBusConnection *connection)
+{
+ DBusDispatchStatus status;
+
+ _dbus_return_val_if_fail (connection != NULL, DBUS_DISPATCH_COMPLETE);
+
+ _dbus_verbose ("start\n");
+
+ CONNECTION_LOCK (connection);
+
+ status = _dbus_connection_get_dispatch_status_unlocked (connection);
+
+ CONNECTION_UNLOCK (connection);
+
+ return status;
+}
+
+/**
+ * Filter funtion for handling the Peer standard interface.
+ */
+static DBusHandlerResult
+_dbus_connection_peer_filter_unlocked_no_update (DBusConnection *connection,
+ DBusMessage *message)
+{
+ dbus_bool_t sent = FALSE;
+ DBusMessage *ret = NULL;
+ DBusList *expire_link;
+
+ if (connection->route_peer_messages && dbus_message_get_destination (message) != NULL)
+ {
+ /* This means we're letting the bus route this message */
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+
+ if (!dbus_message_has_interface (message, DBUS_INTERFACE_PEER))
+ {
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+
+ /* Preallocate a linked-list link, so that if we need to dispose of a
+ * message, we can attach it to the expired list */
+ expire_link = _dbus_list_alloc_link (NULL);
+
+ if (!expire_link)
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+ if (dbus_message_is_method_call (message,
+ DBUS_INTERFACE_PEER,
+ "Ping"))
+ {
+ ret = dbus_message_new_method_return (message);
+ if (ret == NULL)
+ goto out;
+
+ sent = _dbus_connection_send_unlocked_no_update (connection, ret, NULL);
+ }
+ else if (dbus_message_is_method_call (message,
+ DBUS_INTERFACE_PEER,
+ "GetMachineId"))
+ {
+ DBusString uuid;
+ DBusError error = DBUS_ERROR_INIT;
+
+ if (!_dbus_string_init (&uuid))
+ goto out;
+
+ if (_dbus_get_local_machine_uuid_encoded (&uuid, &error))
+ {
+ const char *v_STRING;
+
+ ret = dbus_message_new_method_return (message);
+
+ if (ret == NULL)
+ {
+ _dbus_string_free (&uuid);
+ goto out;
+ }
+
+ v_STRING = _dbus_string_get_const_data (&uuid);
+ if (dbus_message_append_args (ret,
+ DBUS_TYPE_STRING, &v_STRING,
+ DBUS_TYPE_INVALID))
+ {
+ sent = _dbus_connection_send_unlocked_no_update (connection, ret, NULL);
+ }
+ }
+ else if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
+ {
+ dbus_error_free (&error);
+ goto out;
+ }
+ else
+ {
+ ret = dbus_message_new_error (message, error.name, error.message);
+ dbus_error_free (&error);
+
+ if (ret == NULL)
+ goto out;
+
+ sent = _dbus_connection_send_unlocked_no_update (connection, ret,
+ NULL);
+ }
+
+ _dbus_string_free (&uuid);
+ }
+ else
+ {
+ /* We need to bounce anything else with this interface, otherwise apps
+ * could start extending the interface and when we added extensions
+ * here to DBusConnection we'd break those apps.
+ */
+ ret = dbus_message_new_error (message,
+ DBUS_ERROR_UNKNOWN_METHOD,
+ "Unknown method invoked on org.freedesktop.DBus.Peer interface");
+ if (ret == NULL)
+ goto out;
+
+ sent = _dbus_connection_send_unlocked_no_update (connection, ret, NULL);
+ }
+
+out:
+ if (ret == NULL)
+ {
+ _dbus_list_free_link (expire_link);
+ }
+ else
+ {
+ /* It'll be safe to unref the reply when we unlock */
+ expire_link->data = ret;
+ _dbus_list_prepend_link (&connection->expired_messages, expire_link);
+ }
+
+ if (!sent)
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+/**
+* Processes all builtin filter functions
+*
+* If the spec specifies a standard interface
+* they should be processed from this method
+**/
+static DBusHandlerResult
+_dbus_connection_run_builtin_filters_unlocked_no_update (DBusConnection *connection,
+ DBusMessage *message)
+{
+ /* We just run one filter for now but have the option to run more
+ if the spec calls for it in the future */
+
+ return _dbus_connection_peer_filter_unlocked_no_update (connection, message);
+}
+
+/**
+ * Processes any incoming data.
+ *
+ * If there's incoming raw data that has not yet been parsed, it is
+ * parsed, which may or may not result in adding messages to the
+ * incoming queue.
+ *
+ * The incoming data buffer is filled when the connection reads from
+ * its underlying transport (such as a socket). Reading usually
+ * happens in dbus_watch_handle() or dbus_connection_read_write().
+ *
+ * If there are complete messages in the incoming queue,
+ * dbus_connection_dispatch() removes one message from the queue and
+ * processes it. Processing has three steps.
+ *
+ * First, any method replies are passed to #DBusPendingCall or
+ * dbus_connection_send_with_reply_and_block() in order to
+ * complete the pending method call.
+ *
+ * Second, any filters registered with dbus_connection_add_filter()
+ * are run. If any filter returns #DBUS_HANDLER_RESULT_HANDLED
+ * then processing stops after that filter.
+ *
+ * Third, if the message is a method call it is forwarded to
+ * any registered object path handlers added with
+ * dbus_connection_register_object_path() or
+ * dbus_connection_register_fallback().
+ *
+ * A single call to dbus_connection_dispatch() will process at most
+ * one message; it will not clear the entire message queue.
+ *
+ * Be careful about calling dbus_connection_dispatch() from inside a
+ * message handler, i.e. calling dbus_connection_dispatch()
+ * recursively. If threads have been initialized with a recursive
+ * mutex function, then this will not deadlock; however, it can
+ * certainly confuse your application.
+ *
+ * @todo some FIXME in here about handling DBUS_HANDLER_RESULT_NEED_MEMORY
+ *
+ * @param connection the connection
+ * @returns dispatch status, see dbus_connection_get_dispatch_status()
+ */
+DBusDispatchStatus
+dbus_connection_dispatch (DBusConnection *connection)
+{
+ DBusMessage *message;
+ DBusList *link, *filter_list_copy, *message_link;
+ DBusHandlerResult result;
+ DBusPendingCall *pending;
+ dbus_int32_t reply_serial;
+ DBusDispatchStatus status;
+ dbus_bool_t found_object;
+
+ _dbus_return_val_if_fail (connection != NULL, DBUS_DISPATCH_COMPLETE);
+
+ _dbus_verbose ("\n");
+
+ CONNECTION_LOCK (connection);
+ status = _dbus_connection_get_dispatch_status_unlocked (connection);
+ if (status != DBUS_DISPATCH_DATA_REMAINS)
+ {
+ /* unlocks and calls out to user code */
+ _dbus_connection_update_dispatch_status_and_unlock (connection, status);
+ return status;
+ }
+
+ /* We need to ref the connection since the callback could potentially
+ * drop the last ref to it
+ */
+ _dbus_connection_ref_unlocked (connection);
+
+ _dbus_connection_acquire_dispatch (connection);
+ HAVE_LOCK_CHECK (connection);
+
+ message_link = _dbus_connection_pop_message_link_unlocked (connection);
+ if (message_link == NULL)
+ {
+ /* another thread dispatched our stuff */
+
+ _dbus_verbose ("another thread dispatched message (during acquire_dispatch above)\n");
+
+ _dbus_connection_release_dispatch (connection);
+
+ status = _dbus_connection_get_dispatch_status_unlocked (connection);
+
+ _dbus_connection_update_dispatch_status_and_unlock (connection, status);
+
+ dbus_connection_unref (connection);
+
+ return status;
+ }
+
+ message = message_link->data;
+
+ _dbus_verbose (" dispatching message %p (%s %s %s '%s')\n",
+ message,
+ dbus_message_type_to_string (dbus_message_get_type (message)),
+ dbus_message_get_interface (message) ?
+ dbus_message_get_interface (message) :
+ "no interface",
+ dbus_message_get_member (message) ?
+ dbus_message_get_member (message) :
+ "no member",
+ dbus_message_get_signature (message));
+
+ result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ /* Pending call handling must be first, because if you do
+ * dbus_connection_send_with_reply_and_block() or
+ * dbus_pending_call_block() then no handlers/filters will be run on
+ * the reply. We want consistent semantics in the case where we
+ * dbus_connection_dispatch() the reply.
+ */
+
+ reply_serial = dbus_message_get_reply_serial (message);
+ pending = _dbus_hash_table_lookup_int (connection->pending_replies,
+ reply_serial);
+ if (pending)
+ {
+ _dbus_verbose ("Dispatching a pending reply\n");
+ complete_pending_call_and_unlock (connection, pending, message);
+ pending = NULL; /* it's probably unref'd */
+
+ CONNECTION_LOCK (connection);
+ _dbus_verbose ("pending call completed in dispatch\n");
+ result = DBUS_HANDLER_RESULT_HANDLED;
+ goto out;
+ }
+
+ /* If skipping builtin filters, we are probably a monitor. */
+ if (connection->builtin_filters_enabled)
+ {
+ result = _dbus_connection_run_builtin_filters_unlocked_no_update (connection, message);
+ if (result != DBUS_HANDLER_RESULT_NOT_YET_HANDLED)
+ goto out;
+ }
+
+ if (!_dbus_list_copy (&connection->filter_list, &filter_list_copy))
+ {
+ _dbus_connection_release_dispatch (connection);
+ HAVE_LOCK_CHECK (connection);
+
+ _dbus_connection_failed_pop (connection, message_link);
+
+ /* unlocks and calls user code */
+ _dbus_connection_update_dispatch_status_and_unlock (connection,
+ DBUS_DISPATCH_NEED_MEMORY);
+ dbus_connection_unref (connection);
+
+ return DBUS_DISPATCH_NEED_MEMORY;
+ }
+
+ for (link = _dbus_list_get_first_link (&filter_list_copy);
+ link != NULL;
+ link = _dbus_list_get_next_link (&filter_list_copy, link))
+ _dbus_message_filter_ref (link->data);
+
+ /* We're still protected from dispatch() reentrancy here
+ * since we acquired the dispatcher
+ */
+ CONNECTION_UNLOCK (connection);
+
+ link = _dbus_list_get_first_link (&filter_list_copy);
+ while (link != NULL)
+ {
+ DBusMessageFilter *filter = link->data;
+ DBusList *next = _dbus_list_get_next_link (&filter_list_copy, link);
+
+ if (filter->function == NULL)
+ {
+ _dbus_verbose (" filter was removed in a callback function\n");
+ link = next;
+ continue;
+ }
+
+ _dbus_verbose (" running filter on message %p\n", message);
+ result = (* filter->function) (connection, message, filter->user_data);
+
+ if (result != DBUS_HANDLER_RESULT_NOT_YET_HANDLED)
+ break;
+
+ link = next;
+ }
+
+ _dbus_list_clear_full (&filter_list_copy,
+ (DBusFreeFunction) _dbus_message_filter_unref);
+
+ CONNECTION_LOCK (connection);
+
+ if (result == DBUS_HANDLER_RESULT_NEED_MEMORY)
+ {
+ _dbus_verbose ("No memory\n");
+ goto out;
+ }
+ else if (result == DBUS_HANDLER_RESULT_HANDLED)
+ {
+ _dbus_verbose ("filter handled message in dispatch\n");
+ goto out;
+ }
+
+ /* We're still protected from dispatch() reentrancy here
+ * since we acquired the dispatcher
+ */
+ _dbus_verbose (" running object path dispatch on message %p (%s %s %s '%s')\n",
+ message,
+ dbus_message_type_to_string (dbus_message_get_type (message)),
+ dbus_message_get_interface (message) ?
+ dbus_message_get_interface (message) :
+ "no interface",
+ dbus_message_get_member (message) ?
+ dbus_message_get_member (message) :
+ "no member",
+ dbus_message_get_signature (message));
+
+ HAVE_LOCK_CHECK (connection);
+ result = _dbus_object_tree_dispatch_and_unlock (connection->objects,
+ message,
+ &found_object);
+
+ CONNECTION_LOCK (connection);
+
+ if (result != DBUS_HANDLER_RESULT_NOT_YET_HANDLED)
+ {
+ _dbus_verbose ("object tree handled message in dispatch\n");
+ goto out;
+ }
+
+ if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_METHOD_CALL)
+ {
+ DBusMessage *reply;
+ DBusString str;
+ DBusPreallocatedSend *preallocated;
+ DBusList *expire_link;
+
+ _dbus_verbose (" sending error %s\n",
+ DBUS_ERROR_UNKNOWN_METHOD);
+
+ if (!_dbus_string_init (&str))
+ {
+ result = DBUS_HANDLER_RESULT_NEED_MEMORY;
+ _dbus_verbose ("no memory for error string in dispatch\n");
+ goto out;
+ }
+
+ if (!_dbus_string_append_printf (&str,
+ "Method \"%s\" with signature \"%s\" on interface \"%s\" doesn't exist\n",
+ dbus_message_get_member (message),
+ dbus_message_get_signature (message),
+ dbus_message_get_interface (message)))
+ {
+ _dbus_string_free (&str);
+ result = DBUS_HANDLER_RESULT_NEED_MEMORY;
+ _dbus_verbose ("no memory for error string in dispatch\n");
+ goto out;
+ }
+
+ reply = dbus_message_new_error (message,
+ found_object ? DBUS_ERROR_UNKNOWN_METHOD : DBUS_ERROR_UNKNOWN_OBJECT,
+ _dbus_string_get_const_data (&str));
+ _dbus_string_free (&str);
+
+ if (reply == NULL)
+ {
+ result = DBUS_HANDLER_RESULT_NEED_MEMORY;
+ _dbus_verbose ("no memory for error reply in dispatch\n");
+ goto out;
+ }
+
+ expire_link = _dbus_list_alloc_link (reply);
+
+ if (expire_link == NULL)
+ {
+ dbus_message_unref (reply);
+ result = DBUS_HANDLER_RESULT_NEED_MEMORY;
+ _dbus_verbose ("no memory for error send in dispatch\n");
+ goto out;
+ }
+
+ preallocated = _dbus_connection_preallocate_send_unlocked (connection);
+
+ if (preallocated == NULL)
+ {
+ _dbus_list_free_link (expire_link);
+ /* It's OK that this is finalized, because it hasn't been seen by
+ * anything that could attach user callbacks */
+ dbus_message_unref (reply);
+ result = DBUS_HANDLER_RESULT_NEED_MEMORY;
+ _dbus_verbose ("no memory for error send in dispatch\n");
+ goto out;
+ }
+
+ _dbus_connection_send_preallocated_unlocked_no_update (connection, preallocated,
+ reply, NULL);
+ /* reply will be freed when we release the lock */
+ _dbus_list_prepend_link (&connection->expired_messages, expire_link);
+
+ result = DBUS_HANDLER_RESULT_HANDLED;
+ }
+
+ _dbus_verbose (" done dispatching %p (%s %s %s '%s') on connection %p\n", message,
+ dbus_message_type_to_string (dbus_message_get_type (message)),
+ dbus_message_get_interface (message) ?
+ dbus_message_get_interface (message) :
+ "no interface",
+ dbus_message_get_member (message) ?
+ dbus_message_get_member (message) :
+ "no member",
+ dbus_message_get_signature (message),
+ connection);
+
+ out:
+ if (result == DBUS_HANDLER_RESULT_NEED_MEMORY)
+ {
+ _dbus_verbose ("out of memory\n");
+
+ /* Put message back, and we'll start over.
+ * Yes this means handlers must be idempotent if they
+ * don't return HANDLED; c'est la vie.
+ */
+ _dbus_connection_putback_message_link_unlocked (connection,
+ message_link);
+ /* now we don't want to free them */
+ message_link = NULL;
+ message = NULL;
+ }
+ else
+ {
+ _dbus_verbose (" ... done dispatching\n");
+ }
+
+ _dbus_connection_release_dispatch (connection);
+ HAVE_LOCK_CHECK (connection);
+
+ if (message != NULL)
+ {
+ /* We don't want this message to count in maximum message limits when
+ * computing the dispatch status, below. We have to drop the lock
+ * temporarily, because finalizing a message can trigger callbacks.
+ *
+ * We have a reference to the connection, and we don't use any cached
+ * pointers to the connection's internals below this point, so it should
+ * be safe to drop the lock and take it back. */
+ CONNECTION_UNLOCK (connection);
+ dbus_message_unref (message);
+ CONNECTION_LOCK (connection);
+ }
+
+ if (message_link != NULL)
+ _dbus_list_free_link (message_link);
+
+ _dbus_verbose ("before final status update\n");
+ status = _dbus_connection_get_dispatch_status_unlocked (connection);
+
+ /* unlocks and calls user code */
+ _dbus_connection_update_dispatch_status_and_unlock (connection, status);
+
+ dbus_connection_unref (connection);
+
+ return status;
+}
+
+/**
+ * Sets the watch functions for the connection. These functions are
+ * responsible for making the application's main loop aware of file
+ * descriptors that need to be monitored for events, using select() or
+ * poll(). When using Qt, typically the DBusAddWatchFunction would
+ * create a QSocketNotifier. When using GLib, the DBusAddWatchFunction
+ * could call g_io_add_watch(), or could be used as part of a more
+ * elaborate GSource. Note that when a watch is added, it may
+ * not be enabled.
+ *
+ * The DBusWatchToggledFunction notifies the application that the
+ * watch has been enabled or disabled. Call dbus_watch_get_enabled()
+ * to check this. A disabled watch should have no effect, and enabled
+ * watch should be added to the main loop. This feature is used
+ * instead of simply adding/removing the watch because
+ * enabling/disabling can be done without memory allocation. The
+ * toggled function may be NULL if a main loop re-queries
+ * dbus_watch_get_enabled() every time anyway.
+ *
+ * The DBusWatch can be queried for the file descriptor to watch using
+ * dbus_watch_get_unix_fd() or dbus_watch_get_socket(), and for the
+ * events to watch for using dbus_watch_get_flags(). The flags
+ * returned by dbus_watch_get_flags() will only contain
+ * DBUS_WATCH_READABLE and DBUS_WATCH_WRITABLE, never
+ * DBUS_WATCH_HANGUP or DBUS_WATCH_ERROR; all watches implicitly
+ * include a watch for hangups, errors, and other exceptional
+ * conditions.
+ *
+ * Once a file descriptor becomes readable or writable, or an exception
+ * occurs, dbus_watch_handle() should be called to
+ * notify the connection of the file descriptor's condition.
+ *
+ * dbus_watch_handle() cannot be called during the
+ * DBusAddWatchFunction, as the connection will not be ready to handle
+ * that watch yet.
+ *
+ * It is not allowed to reference a DBusWatch after it has been passed
+ * to remove_function.
+ *
+ * If #FALSE is returned due to lack of memory, the failure may be due
+ * to a #FALSE return from the new add_function. If so, the
+ * add_function may have been called successfully one or more times,
+ * but the remove_function will also have been called to remove any
+ * successful adds. i.e. if #FALSE is returned the net result
+ * should be that dbus_connection_set_watch_functions() has no effect,
+ * but the add_function and remove_function may have been called.
+ *
+ * @note The thread lock on DBusConnection is held while
+ * watch functions are invoked, so inside these functions you
+ * may not invoke any methods on DBusConnection or it will deadlock.
+ * See the comments in the code or http://lists.freedesktop.org/archives/dbus/2007-July/tread.html#8144
+ * if you encounter this issue and want to attempt writing a patch.
+ *
+ * @param connection the connection.
+ * @param add_function function to begin monitoring a new descriptor.
+ * @param remove_function function to stop monitoring a descriptor.
+ * @param toggled_function function to notify of enable/disable
+ * @param data data to pass to add_function and remove_function.
+ * @param free_data_function function to be called to free the data.
+ * @returns #FALSE on failure (no memory)
+ */
+dbus_bool_t
+dbus_connection_set_watch_functions (DBusConnection *connection,
+ DBusAddWatchFunction add_function,
+ DBusRemoveWatchFunction remove_function,
+ DBusWatchToggledFunction toggled_function,
+ void *data,
+ DBusFreeFunction free_data_function)
+{
+ dbus_bool_t retval;
+
+ _dbus_return_val_if_fail (connection != NULL, FALSE);
+
+ CONNECTION_LOCK (connection);
+
+ retval = _dbus_watch_list_set_functions (connection->watches,
+ add_function, remove_function,
+ toggled_function,
+ data, free_data_function);
+
+ CONNECTION_UNLOCK (connection);
+
+ return retval;
+}
+
+/**
+ * Sets the timeout functions for the connection. These functions are
+ * responsible for making the application's main loop aware of timeouts.
+ * When using Qt, typically the DBusAddTimeoutFunction would create a
+ * QTimer. When using GLib, the DBusAddTimeoutFunction would call
+ * g_timeout_add.
+ *
+ * The DBusTimeoutToggledFunction notifies the application that the
+ * timeout has been enabled or disabled. Call
+ * dbus_timeout_get_enabled() to check this. A disabled timeout should
+ * have no effect, and enabled timeout should be added to the main
+ * loop. This feature is used instead of simply adding/removing the
+ * timeout because enabling/disabling can be done without memory
+ * allocation. With Qt, QTimer::start() and QTimer::stop() can be used
+ * to enable and disable. The toggled function may be NULL if a main
+ * loop re-queries dbus_timeout_get_enabled() every time anyway.
+ * Whenever a timeout is toggled, its interval may change.
+ *
+ * The DBusTimeout can be queried for the timer interval using
+ * dbus_timeout_get_interval(). dbus_timeout_handle() should be called
+ * repeatedly, each time the interval elapses, starting after it has
+ * elapsed once. The timeout stops firing when it is removed with the
+ * given remove_function. The timer interval may change whenever the
+ * timeout is added, removed, or toggled.
+ *
+ * @note The thread lock on DBusConnection is held while
+ * timeout functions are invoked, so inside these functions you
+ * may not invoke any methods on DBusConnection or it will deadlock.
+ * See the comments in the code or http://lists.freedesktop.org/archives/dbus/2007-July/thread.html#8144
+ * if you encounter this issue and want to attempt writing a patch.
+ *
+ * @param connection the connection.
+ * @param add_function function to add a timeout.
+ * @param remove_function function to remove a timeout.
+ * @param toggled_function function to notify of enable/disable
+ * @param data data to pass to add_function and remove_function.
+ * @param free_data_function function to be called to free the data.
+ * @returns #FALSE on failure (no memory)
+ */
+dbus_bool_t
+dbus_connection_set_timeout_functions (DBusConnection *connection,
+ DBusAddTimeoutFunction add_function,
+ DBusRemoveTimeoutFunction remove_function,
+ DBusTimeoutToggledFunction toggled_function,
+ void *data,
+ DBusFreeFunction free_data_function)
+{
+ dbus_bool_t retval;
+
+ _dbus_return_val_if_fail (connection != NULL, FALSE);
+
+ CONNECTION_LOCK (connection);
+
+ retval = _dbus_timeout_list_set_functions (connection->timeouts,
+ add_function, remove_function,
+ toggled_function,
+ data, free_data_function);
+
+ CONNECTION_UNLOCK (connection);
+
+ return retval;
+}
+
+/**
+ * Sets the mainloop wakeup function for the connection. This function
+ * is responsible for waking up the main loop (if its sleeping in
+ * another thread) when some some change has happened to the
+ * connection that the mainloop needs to reconsider (e.g. a message
+ * has been queued for writing). When using Qt, this typically
+ * results in a call to QEventLoop::wakeUp(). When using GLib, it
+ * would call g_main_context_wakeup().
+ *
+ * @param connection the connection.
+ * @param wakeup_main_function function to wake up the mainloop
+ * @param data data to pass wakeup_main_function
+ * @param free_data_function function to be called to free the data.
+ */
+void
+dbus_connection_set_wakeup_main_function (DBusConnection *connection,
+ DBusWakeupMainFunction wakeup_main_function,
+ void *data,
+ DBusFreeFunction free_data_function)
+{
+ void *old_data;
+ DBusFreeFunction old_free_data;
+
+ _dbus_return_if_fail (connection != NULL);
+
+ CONNECTION_LOCK (connection);
+ old_data = connection->wakeup_main_data;
+ old_free_data = connection->free_wakeup_main_data;
+
+ connection->wakeup_main_function = wakeup_main_function;
+ connection->wakeup_main_data = data;
+ connection->free_wakeup_main_data = free_data_function;
+
+ CONNECTION_UNLOCK (connection);
+
+ /* Callback outside the lock */
+ if (old_free_data)
+ (*old_free_data) (old_data);
+}
+
+/**
+ * Set a function to be invoked when the dispatch status changes.
+ * If the dispatch status is #DBUS_DISPATCH_DATA_REMAINS, then
+ * dbus_connection_dispatch() needs to be called to process incoming
+ * messages. However, dbus_connection_dispatch() MUST NOT BE CALLED
+ * from inside the DBusDispatchStatusFunction. Indeed, almost
+ * any reentrancy in this function is a bad idea. Instead,
+ * the DBusDispatchStatusFunction should simply save an indication
+ * that messages should be dispatched later, when the main loop
+ * is re-entered.
+ *
+ * If you don't set a dispatch status function, you have to be sure to
+ * dispatch on every iteration of your main loop, especially if
+ * dbus_watch_handle() or dbus_timeout_handle() were called.
+ *
+ * @param connection the connection
+ * @param function function to call on dispatch status changes
+ * @param data data for function
+ * @param free_data_function free the function data
+ */
+void
+dbus_connection_set_dispatch_status_function (DBusConnection *connection,
+ DBusDispatchStatusFunction function,
+ void *data,
+ DBusFreeFunction free_data_function)
+{
+ void *old_data;
+ DBusFreeFunction old_free_data;
+
+ _dbus_return_if_fail (connection != NULL);
+
+ CONNECTION_LOCK (connection);
+ old_data = connection->dispatch_status_data;
+ old_free_data = connection->free_dispatch_status_data;
+
+ connection->dispatch_status_function = function;
+ connection->dispatch_status_data = data;
+ connection->free_dispatch_status_data = free_data_function;
+
+ CONNECTION_UNLOCK (connection);
+
+ /* Callback outside the lock */
+ if (old_free_data)
+ (*old_free_data) (old_data);
+}
+
+/**
+ * Get the UNIX file descriptor of the connection, if any. This can
+ * be used for SELinux access control checks with getpeercon() for
+ * example. DO NOT read or write to the file descriptor, or try to
+ * select() on it; use DBusWatch for main loop integration. Not all
+ * connections will have a file descriptor. So for adding descriptors
+ * to the main loop, use dbus_watch_get_unix_fd() and so forth.
+ *
+ * If the connection is socket-based, you can also use
+ * dbus_connection_get_socket(), which will work on Windows too.
+ * This function always fails on Windows.
+ *
+ * Right now the returned descriptor is always a socket, but
+ * that is not guaranteed.
+ *
+ * @param connection the connection
+ * @param fd return location for the file descriptor.
+ * @returns #TRUE if fd is successfully obtained.
+ */
+dbus_bool_t
+dbus_connection_get_unix_fd (DBusConnection *connection,
+ int *fd)
+{
+ _dbus_return_val_if_fail (connection != NULL, FALSE);
+ _dbus_return_val_if_fail (connection->transport != NULL, FALSE);
+
+#ifdef DBUS_WIN
+ /* FIXME do this on a lower level */
+ return FALSE;
+#endif
+
+ return dbus_connection_get_socket(connection, fd);
+}
+
+/**
+ * Gets the underlying Windows or UNIX socket file descriptor
+ * of the connection, if any. DO NOT read or write to the file descriptor, or try to
+ * select() on it; use DBusWatch for main loop integration. Not all
+ * connections will have a socket. So for adding descriptors
+ * to the main loop, use dbus_watch_get_socket() and so forth.
+ *
+ * If the connection is not socket-based, this function will return FALSE,
+ * even if the connection does have a file descriptor of some kind.
+ * i.e. this function always returns specifically a socket file descriptor.
+ *
+ * @param connection the connection
+ * @param fd return location for the file descriptor.
+ * @returns #TRUE if fd is successfully obtained.
+ */
+dbus_bool_t
+dbus_connection_get_socket(DBusConnection *connection,
+ int *fd)
+{
+ dbus_bool_t retval;
+ DBusSocket s = DBUS_SOCKET_INIT;
+
+ _dbus_return_val_if_fail (connection != NULL, FALSE);
+ _dbus_return_val_if_fail (connection->transport != NULL, FALSE);
+
+ CONNECTION_LOCK (connection);
+
+ retval = _dbus_transport_get_socket_fd (connection->transport, &s);
+
+ if (retval)
+ {
+ *fd = _dbus_socket_get_int (s);
+ }
+
+ CONNECTION_UNLOCK (connection);
+
+ return retval;
+}
+
+
+/**
+ * Gets the UNIX user ID of the connection if known. Returns #TRUE if
+ * the uid is filled in. Always returns #FALSE on non-UNIX platforms
+ * for now, though in theory someone could hook Windows to NIS or
+ * something. Always returns #FALSE prior to authenticating the
+ * connection.
+ *
+ * The UID is only read by servers from clients; clients can't usually
+ * get the UID of servers, because servers do not authenticate to
+ * clients. The returned UID is the UID the connection authenticated
+ * as.
+ *
+ * The message bus is a server and the apps connecting to the bus
+ * are clients.
+ *
+ * You can ask the bus to tell you the UID of another connection though
+ * if you like; this is done with dbus_bus_get_unix_user().
+ *
+ * @param connection the connection
+ * @param uid return location for the user ID
+ * @returns #TRUE if uid is filled in with a valid user ID
+ */
+dbus_bool_t
+dbus_connection_get_unix_user (DBusConnection *connection,
+ unsigned long *uid)
+{
+ dbus_bool_t result;
+
+ _dbus_return_val_if_fail (connection != NULL, FALSE);
+ _dbus_return_val_if_fail (uid != NULL, FALSE);
+
+ CONNECTION_LOCK (connection);
+
+ if (!_dbus_transport_try_to_authenticate (connection->transport))
+ result = FALSE;
+ else
+ result = _dbus_transport_get_unix_user (connection->transport,
+ uid);
+
+#ifdef DBUS_WIN
+ _dbus_assert (!result);
+#endif
+
+ CONNECTION_UNLOCK (connection);
+
+ return result;
+}
+
+/**
+ * Gets the process ID of the connection if any.
+ * Returns #TRUE if the pid is filled in.
+ * Always returns #FALSE prior to authenticating the
+ * connection.
+ *
+ * @param connection the connection
+ * @param pid return location for the process ID
+ * @returns #TRUE if uid is filled in with a valid process ID
+ */
+dbus_bool_t
+dbus_connection_get_unix_process_id (DBusConnection *connection,
+ unsigned long *pid)
+{
+ dbus_bool_t result;
+
+ _dbus_return_val_if_fail (connection != NULL, FALSE);
+ _dbus_return_val_if_fail (pid != NULL, FALSE);
+
+ CONNECTION_LOCK (connection);
+
+ if (!_dbus_transport_try_to_authenticate (connection->transport))
+ result = FALSE;
+ else
+ result = _dbus_transport_get_unix_process_id (connection->transport,
+ pid);
+
+ CONNECTION_UNLOCK (connection);
+
+ return result;
+}
+
+/**
+ * Gets the ADT audit data of the connection if any.
+ * Returns #TRUE if the structure pointer is returned.
+ * Always returns #FALSE prior to authenticating the
+ * connection.
+ *
+ * @param connection the connection
+ * @param data return location for audit data
+ * @param data_size return location for length of audit data
+ * @returns #TRUE if audit data is filled in with a valid ucred pointer
+ */
+dbus_bool_t
+dbus_connection_get_adt_audit_session_data (DBusConnection *connection,
+ void **data,
+ dbus_int32_t *data_size)
+{
+ dbus_bool_t result;
+
+ _dbus_return_val_if_fail (connection != NULL, FALSE);
+ _dbus_return_val_if_fail (data != NULL, FALSE);
+ _dbus_return_val_if_fail (data_size != NULL, FALSE);
+
+ CONNECTION_LOCK (connection);
+
+ if (!_dbus_transport_try_to_authenticate (connection->transport))
+ result = FALSE;
+ else
+ result = _dbus_transport_get_adt_audit_session_data (connection->transport,
+ data,
+ data_size);
+ CONNECTION_UNLOCK (connection);
+
+ return result;
+}
+
+/**
+ * Sets a predicate function used to determine whether a given user ID
+ * is allowed to connect. When an incoming connection has
+ * authenticated with a particular user ID, this function is called;
+ * if it returns #TRUE, the connection is allowed to proceed,
+ * otherwise the connection is disconnected.
+ *
+ * If the function is set to #NULL (as it is by default), then
+ * only the same UID as the server process will be allowed to
+ * connect. Also, root is always allowed to connect.
+ *
+ * On Windows, the function will be set and its free_data_function will
+ * be invoked when the connection is freed or a new function is set.
+ * However, the function will never be called, because there are
+ * no UNIX user ids to pass to it, or at least none of the existing
+ * auth protocols would allow authenticating as a UNIX user on Windows.
+ *
+ * @param connection the connection
+ * @param function the predicate
+ * @param data data to pass to the predicate
+ * @param free_data_function function to free the data
+ */
+void
+dbus_connection_set_unix_user_function (DBusConnection *connection,
+ DBusAllowUnixUserFunction function,
+ void *data,
+ DBusFreeFunction free_data_function)
+{
+ void *old_data = NULL;
+ DBusFreeFunction old_free_function = NULL;
+
+ _dbus_return_if_fail (connection != NULL);
+
+ CONNECTION_LOCK (connection);
+ _dbus_transport_set_unix_user_function (connection->transport,
+ function, data, free_data_function,
+ &old_data, &old_free_function);
+ CONNECTION_UNLOCK (connection);
+
+ if (old_free_function != NULL)
+ (* old_free_function) (old_data);
+}
+
+/* Same calling convention as dbus_connection_get_windows_user */
+dbus_bool_t
+_dbus_connection_get_linux_security_label (DBusConnection *connection,
+ char **label_p)
+{
+ dbus_bool_t result;
+
+ _dbus_assert (connection != NULL);
+ _dbus_assert (label_p != NULL);
+
+ CONNECTION_LOCK (connection);
+
+ if (!_dbus_transport_try_to_authenticate (connection->transport))
+ result = FALSE;
+ else
+ result = _dbus_transport_get_linux_security_label (connection->transport,
+ label_p);
+#ifndef __linux__
+ _dbus_assert (!result);
+#endif
+
+ CONNECTION_UNLOCK (connection);
+
+ return result;
+}
+
+DBusCredentials *
+_dbus_connection_get_credentials (DBusConnection *connection)
+{
+ DBusCredentials *result;
+
+ _dbus_assert (connection != NULL);
+
+ CONNECTION_LOCK (connection);
+
+ if (!_dbus_transport_try_to_authenticate (connection->transport))
+ result = NULL;
+ else
+ result = _dbus_transport_get_credentials (connection->transport);
+
+ CONNECTION_UNLOCK (connection);
+
+ return result;
+}
+
+/**
+ * Gets the Windows user SID of the connection if known. Returns
+ * #TRUE if the ID is filled in. Always returns #FALSE on non-Windows
+ * platforms for now, though in theory someone could hook UNIX to
+ * Active Directory or something. Always returns #FALSE prior to
+ * authenticating the connection.
+ *
+ * The user is only read by servers from clients; clients can't usually
+ * get the user of servers, because servers do not authenticate to
+ * clients. The returned user is the user the connection authenticated
+ * as.
+ *
+ * The message bus is a server and the apps connecting to the bus
+ * are clients.
+ *
+ * The returned user string has to be freed with dbus_free().
+ *
+ * The return value indicates whether the user SID is available;
+ * if it's available but we don't have the memory to copy it,
+ * then the return value is #TRUE and #NULL is given as the SID.
+ *
+ * @todo We would like to be able to say "You can ask the bus to tell
+ * you the user of another connection though if you like; this is done
+ * with dbus_bus_get_windows_user()." But this has to be implemented
+ * in bus/driver.c and dbus/dbus-bus.c, and is pointless anyway
+ * since on Windows we only use the session bus for now.
+ *
+ * @param connection the connection
+ * @param windows_sid_p return location for an allocated copy of the user ID, or #NULL if no memory
+ * @returns #TRUE if user is available (returned value may be #NULL anyway if no memory)
+ */
+dbus_bool_t
+dbus_connection_get_windows_user (DBusConnection *connection,
+ char **windows_sid_p)
+{
+ dbus_bool_t result;
+
+ _dbus_return_val_if_fail (connection != NULL, FALSE);
+ _dbus_return_val_if_fail (windows_sid_p != NULL, FALSE);
+
+ CONNECTION_LOCK (connection);
+
+ if (!_dbus_transport_try_to_authenticate (connection->transport))
+ result = FALSE;
+ else
+ result = _dbus_transport_get_windows_user (connection->transport,
+ windows_sid_p);
+
+#ifdef DBUS_UNIX
+ _dbus_assert (!result);
+#endif
+
+ CONNECTION_UNLOCK (connection);
+
+ return result;
+}
+
+/**
+ * Sets a predicate function used to determine whether a given user ID
+ * is allowed to connect. When an incoming connection has
+ * authenticated with a particular user ID, this function is called;
+ * if it returns #TRUE, the connection is allowed to proceed,
+ * otherwise the connection is disconnected.
+ *
+ * If the function is set to #NULL (as it is by default), then
+ * only the same user owning the server process will be allowed to
+ * connect.
+ *
+ * On UNIX, the function will be set and its free_data_function will
+ * be invoked when the connection is freed or a new function is set.
+ * However, the function will never be called, because there is no
+ * way right now to authenticate as a Windows user on UNIX.
+ *
+ * @param connection the connection
+ * @param function the predicate
+ * @param data data to pass to the predicate
+ * @param free_data_function function to free the data
+ */
+void
+dbus_connection_set_windows_user_function (DBusConnection *connection,
+ DBusAllowWindowsUserFunction function,
+ void *data,
+ DBusFreeFunction free_data_function)
+{
+ void *old_data = NULL;
+ DBusFreeFunction old_free_function = NULL;
+
+ _dbus_return_if_fail (connection != NULL);
+
+ CONNECTION_LOCK (connection);
+ _dbus_transport_set_windows_user_function (connection->transport,
+ function, data, free_data_function,
+ &old_data, &old_free_function);
+ CONNECTION_UNLOCK (connection);
+
+ if (old_free_function != NULL)
+ (* old_free_function) (old_data);
+}
+
+/**
+ * This function must be called on the server side of a connection when the
+ * connection is first seen in the #DBusNewConnectionFunction. If set to
+ * #TRUE (the default is #FALSE), then the connection can proceed even if
+ * the client does not authenticate as some user identity, i.e. clients
+ * can connect anonymously.
+ *
+ * This setting interacts with the available authorization mechanisms
+ * (see dbus_server_set_auth_mechanisms()). Namely, an auth mechanism
+ * such as ANONYMOUS that supports anonymous auth must be included in
+ * the list of available mechanisms for anonymous login to work.
+ *
+ * This setting also changes the default rule for connections
+ * authorized as a user; normally, if a connection authorizes as
+ * a user identity, it is permitted if the user identity is
+ * root or the user identity matches the user identity of the server
+ * process. If anonymous connections are allowed, however,
+ * then any user identity is allowed.
+ *
+ * You can override the rules for connections authorized as a
+ * user identity with dbus_connection_set_unix_user_function()
+ * and dbus_connection_set_windows_user_function().
+ *
+ * @param connection the connection
+ * @param value whether to allow authentication as an anonymous user
+ */
+void
+dbus_connection_set_allow_anonymous (DBusConnection *connection,
+ dbus_bool_t value)
+{
+ _dbus_return_if_fail (connection != NULL);
+
+ CONNECTION_LOCK (connection);
+ _dbus_transport_set_allow_anonymous (connection->transport, value);
+ CONNECTION_UNLOCK (connection);
+}
+
+/**
+ * Enables the builtin filtering of messages.
+ *
+ * Currently the only filtering implemented by libdbus and mandated by the spec
+ * is that of peer messages.
+ *
+ * If #TRUE, #DBusConnection automatically handles all messages to the
+ * org.freedesktop.DBus.Peer interface. For monitors this can break the
+ * specification if the response is sending a message.
+ *
+ * If #FALSE, the result is similar to calling
+ * dbus_connection_set_route_peer_messages() with argument TRUE, but
+ * messages with a NULL destination are also dispatched to the
+ * application instead of being passed to the built-in filters.
+ *
+ * If a normal application disables this flag, it can break things badly. So
+ * only unset this if you are a monitor.
+ *
+ * @param connection the connection
+ * @param value #TRUE to pass through org.freedesktop.DBus.Peer messages
+ */
+void
+dbus_connection_set_builtin_filters_enabled (DBusConnection *connection,
+ dbus_bool_t value)
+{
+ _dbus_return_if_fail (connection != NULL);
+
+ CONNECTION_LOCK (connection);
+ connection->builtin_filters_enabled = value;
+ CONNECTION_UNLOCK (connection);
+}
+
+/**
+ *
+ * Normally #DBusConnection automatically handles all messages to the
+ * org.freedesktop.DBus.Peer interface. However, the message bus wants
+ * to be able to route methods on that interface through the bus and
+ * to other applications. If routing peer messages is enabled, then
+ * messages with the org.freedesktop.DBus.Peer interface that also
+ * have a bus destination name set will not be automatically
+ * handled by the #DBusConnection and instead will be dispatched
+ * normally to the application.
+ *
+ * If a normal application sets this flag, it can break things badly.
+ * So don't set this unless you are the message bus.
+ *
+ * @param connection the connection
+ * @param value #TRUE to pass through org.freedesktop.DBus.Peer messages with a bus name set
+ */
+void
+dbus_connection_set_route_peer_messages (DBusConnection *connection,
+ dbus_bool_t value)
+{
+ _dbus_return_if_fail (connection != NULL);
+
+ CONNECTION_LOCK (connection);
+ connection->route_peer_messages = value;
+ CONNECTION_UNLOCK (connection);
+}
+
+/**
+ * Adds a message filter. Filters are handlers that are run on all
+ * incoming messages, prior to the objects registered with
+ * dbus_connection_register_object_path(). Filters are run in the
+ * order that they were added. The same handler can be added as a
+ * filter more than once, in which case it will be run more than once.
+ * Filters added during a filter callback won't be run on the message
+ * being processed.
+ *
+ * @todo we don't run filters on messages while blocking without
+ * entering the main loop, since filters are run as part of
+ * dbus_connection_dispatch(). This is probably a feature, as filters
+ * could create arbitrary reentrancy. But kind of sucks if you're
+ * trying to filter METHOD_RETURN for some reason.
+ *
+ * @param connection the connection
+ * @param function function to handle messages
+ * @param user_data user data to pass to the function
+ * @param free_data_function function to use for freeing user data
+ * @returns #TRUE on success, #FALSE if not enough memory.
+ */
+dbus_bool_t
+dbus_connection_add_filter (DBusConnection *connection,
+ DBusHandleMessageFunction function,
+ void *user_data,
+ DBusFreeFunction free_data_function)
+{
+ DBusMessageFilter *filter;
+
+ _dbus_return_val_if_fail (connection != NULL, FALSE);
+ _dbus_return_val_if_fail (function != NULL, FALSE);
+
+ filter = dbus_new0 (DBusMessageFilter, 1);
+ if (filter == NULL)
+ return FALSE;
+
+ _dbus_atomic_inc (&filter->refcount);
+
+ CONNECTION_LOCK (connection);
+
+ if (!_dbus_list_append (&connection->filter_list,
+ filter))
+ {
+ _dbus_message_filter_unref (filter);
+ CONNECTION_UNLOCK (connection);
+ return FALSE;
+ }
+
+ /* Fill in filter after all memory allocated,
+ * so we don't run the free_user_data_function
+ * if the add_filter() fails
+ */
+
+ filter->function = function;
+ filter->user_data = user_data;
+ filter->free_user_data_function = free_data_function;
+
+ CONNECTION_UNLOCK (connection);
+ return TRUE;
+}
+
+/**
+ * Removes a previously-added message filter. It is a programming
+ * error to call this function for a handler that has not been added
+ * as a filter. If the given handler was added more than once, only
+ * one instance of it will be removed (the most recently-added
+ * instance).
+ *
+ * @param connection the connection
+ * @param function the handler to remove
+ * @param user_data user data for the handler to remove
+ *
+ */
+void
+dbus_connection_remove_filter (DBusConnection *connection,
+ DBusHandleMessageFunction function,
+ void *user_data)
+{
+ DBusList *link;
+ DBusMessageFilter *filter;
+
+ _dbus_return_if_fail (connection != NULL);
+ _dbus_return_if_fail (function != NULL);
+
+ CONNECTION_LOCK (connection);
+
+ filter = NULL;
+
+ link = _dbus_list_get_last_link (&connection->filter_list);
+ while (link != NULL)
+ {
+ filter = link->data;
+
+ if (filter->function == function &&
+ filter->user_data == user_data)
+ {
+ _dbus_list_remove_link (&connection->filter_list, link);
+ filter->function = NULL;
+
+ break;
+ }
+
+ link = _dbus_list_get_prev_link (&connection->filter_list, link);
+ filter = NULL;
+ }
+
+ CONNECTION_UNLOCK (connection);
+
+#ifndef DBUS_DISABLE_CHECKS
+ if (filter == NULL)
+ {
+ _dbus_warn_check_failed ("Attempt to remove filter function %p user data %p, but no such filter has been added",
+ function, user_data);
+ return;
+ }
+#endif
+
+ /* Call application code */
+ if (filter->free_user_data_function)
+ (* filter->free_user_data_function) (filter->user_data);
+
+ filter->free_user_data_function = NULL;
+ filter->user_data = NULL;
+
+ _dbus_message_filter_unref (filter);
+}
+
+/**
+ * Registers a handler for a given path or subsection in the object
+ * hierarchy. The given vtable handles messages sent to exactly the
+ * given path or also for paths bellow that, depending on fallback
+ * parameter.
+ *
+ * @param connection the connection
+ * @param fallback whether to handle messages also for "subdirectory"
+ * @param path a '/' delimited string of path elements
+ * @param vtable the virtual table
+ * @param user_data data to pass to functions in the vtable
+ * @param error address where an error can be returned
+ * @returns #FALSE if an error (#DBUS_ERROR_NO_MEMORY or
+ * #DBUS_ERROR_OBJECT_PATH_IN_USE) is reported
+ */
+static dbus_bool_t
+_dbus_connection_register_object_path (DBusConnection *connection,
+ dbus_bool_t fallback,
+ const char *path,
+ const DBusObjectPathVTable *vtable,
+ void *user_data,
+ DBusError *error)
+{
+ char **decomposed_path;
+ dbus_bool_t retval;
+
+ if (!_dbus_decompose_path (path, strlen (path), &decomposed_path, NULL))
+ return FALSE;
+
+ CONNECTION_LOCK (connection);
+
+ retval = _dbus_object_tree_register (connection->objects,
+ fallback,
+ (const char **) decomposed_path, vtable,
+ user_data, error);
+
+ CONNECTION_UNLOCK (connection);
+
+ dbus_free_string_array (decomposed_path);
+
+ return retval;
+}
+
+/**
+ * Registers a handler for a given path in the object hierarchy.
+ * The given vtable handles messages sent to exactly the given path.
+ *
+ * @param connection the connection
+ * @param path a '/' delimited string of path elements
+ * @param vtable the virtual table
+ * @param user_data data to pass to functions in the vtable
+ * @param error address where an error can be returned
+ * @returns #FALSE if an error (#DBUS_ERROR_NO_MEMORY or
+ * #DBUS_ERROR_OBJECT_PATH_IN_USE) is reported
+ */
+dbus_bool_t
+dbus_connection_try_register_object_path (DBusConnection *connection,
+ const char *path,
+ const DBusObjectPathVTable *vtable,
+ void *user_data,
+ DBusError *error)
+{
+ _dbus_return_val_if_fail (connection != NULL, FALSE);
+ _dbus_return_val_if_fail (path != NULL, FALSE);
+ _dbus_return_val_if_fail (path[0] == '/', FALSE);
+ _dbus_return_val_if_fail (vtable != NULL, FALSE);
+
+ return _dbus_connection_register_object_path (connection, FALSE, path, vtable, user_data, error);
+}
+
+/**
+ * Registers a handler for a given path in the object hierarchy.
+ * The given vtable handles messages sent to exactly the given path.
+ *
+ * It is a bug to call this function for object paths which already
+ * have a handler. Use dbus_connection_try_register_object_path() if this
+ * might be the case.
+ *
+ * @param connection the connection
+ * @param path a '/' delimited string of path elements
+ * @param vtable the virtual table
+ * @param user_data data to pass to functions in the vtable
+ * @returns #FALSE if an error (#DBUS_ERROR_NO_MEMORY or
+ * #DBUS_ERROR_OBJECT_PATH_IN_USE) ocurred
+ */
+dbus_bool_t
+dbus_connection_register_object_path (DBusConnection *connection,
+ const char *path,
+ const DBusObjectPathVTable *vtable,
+ void *user_data)
+{
+ dbus_bool_t retval;
+ DBusError error = DBUS_ERROR_INIT;
+
+ _dbus_return_val_if_fail (connection != NULL, FALSE);
+ _dbus_return_val_if_fail (path != NULL, FALSE);
+ _dbus_return_val_if_fail (path[0] == '/', FALSE);
+ _dbus_return_val_if_fail (vtable != NULL, FALSE);
+
+ retval = _dbus_connection_register_object_path (connection, FALSE, path, vtable, user_data, &error);
+
+ if (dbus_error_has_name (&error, DBUS_ERROR_OBJECT_PATH_IN_USE))
+ {
+ _dbus_warn ("%s", error.message);
+ dbus_error_free (&error);
+ return FALSE;
+ }
+
+ return retval;
+}
+
+/**
+ * Registers a fallback handler for a given subsection of the object
+ * hierarchy. The given vtable handles messages at or below the given
+ * path. You can use this to establish a default message handling
+ * policy for a whole "subdirectory."
+ *
+ * @param connection the connection
+ * @param path a '/' delimited string of path elements
+ * @param vtable the virtual table
+ * @param user_data data to pass to functions in the vtable
+ * @param error address where an error can be returned
+ * @returns #FALSE if an error (#DBUS_ERROR_NO_MEMORY or
+ * #DBUS_ERROR_OBJECT_PATH_IN_USE) is reported
+ */
+dbus_bool_t
+dbus_connection_try_register_fallback (DBusConnection *connection,
+ const char *path,
+ const DBusObjectPathVTable *vtable,
+ void *user_data,
+ DBusError *error)
+{
+ _dbus_return_val_if_fail (connection != NULL, FALSE);
+ _dbus_return_val_if_fail (path != NULL, FALSE);
+ _dbus_return_val_if_fail (path[0] == '/', FALSE);
+ _dbus_return_val_if_fail (vtable != NULL, FALSE);
+
+ return _dbus_connection_register_object_path (connection, TRUE, path, vtable, user_data, error);
+}
+
+/**
+ * Registers a fallback handler for a given subsection of the object
+ * hierarchy. The given vtable handles messages at or below the given
+ * path. You can use this to establish a default message handling
+ * policy for a whole "subdirectory."
+ *
+ * It is a bug to call this function for object paths which already
+ * have a handler. Use dbus_connection_try_register_fallback() if this
+ * might be the case.
+ *
+ * @param connection the connection
+ * @param path a '/' delimited string of path elements
+ * @param vtable the virtual table
+ * @param user_data data to pass to functions in the vtable
+ * @returns #FALSE if an error (#DBUS_ERROR_NO_MEMORY or
+ * #DBUS_ERROR_OBJECT_PATH_IN_USE) occured
+ */
+dbus_bool_t
+dbus_connection_register_fallback (DBusConnection *connection,
+ const char *path,
+ const DBusObjectPathVTable *vtable,
+ void *user_data)
+{
+ dbus_bool_t retval;
+ DBusError error = DBUS_ERROR_INIT;
+
+ _dbus_return_val_if_fail (connection != NULL, FALSE);
+ _dbus_return_val_if_fail (path != NULL, FALSE);
+ _dbus_return_val_if_fail (path[0] == '/', FALSE);
+ _dbus_return_val_if_fail (vtable != NULL, FALSE);
+
+ retval = _dbus_connection_register_object_path (connection, TRUE, path, vtable, user_data, &error);
+
+ if (dbus_error_has_name (&error, DBUS_ERROR_OBJECT_PATH_IN_USE))
+ {
+ _dbus_warn ("%s", error.message);
+ dbus_error_free (&error);
+ return FALSE;
+ }
+
+ return retval;
+}
+
+/**
+ * Unregisters the handler registered with exactly the given path.
+ * It's a bug to call this function for a path that isn't registered.
+ * Can unregister both fallback paths and object paths.
+ *
+ * @param connection the connection
+ * @param path a '/' delimited string of path elements
+ * @returns #FALSE if not enough memory
+ */
+dbus_bool_t
+dbus_connection_unregister_object_path (DBusConnection *connection,
+ const char *path)
+{
+ char **decomposed_path;
+
+ _dbus_return_val_if_fail (connection != NULL, FALSE);
+ _dbus_return_val_if_fail (path != NULL, FALSE);
+ _dbus_return_val_if_fail (path[0] == '/', FALSE);
+
+ if (!_dbus_decompose_path (path, strlen (path), &decomposed_path, NULL))
+ return FALSE;
+
+ CONNECTION_LOCK (connection);
+
+ _dbus_object_tree_unregister_and_unlock (connection->objects, (const char **) decomposed_path);
+
+ dbus_free_string_array (decomposed_path);
+
+ return TRUE;
+}
+
+/**
+ * Gets the user data passed to dbus_connection_register_object_path()
+ * or dbus_connection_register_fallback(). If nothing was registered
+ * at this path, the data is filled in with #NULL.
+ *
+ * @param connection the connection
+ * @param path the path you registered with
+ * @param data_p location to store the user data, or #NULL
+ * @returns #FALSE if not enough memory
+ */
+dbus_bool_t
+dbus_connection_get_object_path_data (DBusConnection *connection,
+ const char *path,
+ void **data_p)
+{
+ char **decomposed_path;
+
+ _dbus_return_val_if_fail (connection != NULL, FALSE);
+ _dbus_return_val_if_fail (path != NULL, FALSE);
+ _dbus_return_val_if_fail (data_p != NULL, FALSE);
+
+ *data_p = NULL;
+
+ if (!_dbus_decompose_path (path, strlen (path), &decomposed_path, NULL))
+ return FALSE;
+
+ CONNECTION_LOCK (connection);
+
+ *data_p = _dbus_object_tree_get_user_data_unlocked (connection->objects, (const char**) decomposed_path);
+
+ CONNECTION_UNLOCK (connection);
+
+ dbus_free_string_array (decomposed_path);
+
+ return TRUE;
+}
+
+/**
+ * Lists the registered fallback handlers and object path handlers at
+ * the given parent_path. The returned array should be freed with
+ * dbus_free_string_array().
+ *
+ * @param connection the connection
+ * @param parent_path the path to list the child handlers of
+ * @param child_entries returns #NULL-terminated array of children
+ * @returns #FALSE if no memory to allocate the child entries
+ */
+dbus_bool_t
+dbus_connection_list_registered (DBusConnection *connection,
+ const char *parent_path,
+ char ***child_entries)
+{
+ char **decomposed_path;
+ dbus_bool_t retval;
+ _dbus_return_val_if_fail (connection != NULL, FALSE);
+ _dbus_return_val_if_fail (parent_path != NULL, FALSE);
+ _dbus_return_val_if_fail (parent_path[0] == '/', FALSE);
+ _dbus_return_val_if_fail (child_entries != NULL, FALSE);
+
+ if (!_dbus_decompose_path (parent_path, strlen (parent_path), &decomposed_path, NULL))
+ return FALSE;
+
+ CONNECTION_LOCK (connection);
+
+ retval = _dbus_object_tree_list_registered_and_unlock (connection->objects,
+ (const char **) decomposed_path,
+ child_entries);
+ dbus_free_string_array (decomposed_path);
+
+ return retval;
+}
+
+static DBusDataSlotAllocator slot_allocator =
+ _DBUS_DATA_SLOT_ALLOCATOR_INIT (_DBUS_LOCK_NAME (connection_slots));
+
+/**
+ * Allocates an integer ID to be used for storing application-specific
+ * data on any DBusConnection. The allocated ID may then be used
+ * with dbus_connection_set_data() and dbus_connection_get_data().
+ * The passed-in slot must be initialized to -1, and is filled in
+ * with the slot ID. If the passed-in slot is not -1, it's assumed
+ * to be already allocated, and its refcount is incremented.
+ *
+ * The allocated slot is global, i.e. all DBusConnection objects will
+ * have a slot with the given integer ID reserved.
+ *
+ * @param slot_p address of a global variable storing the slot
+ * @returns #FALSE on failure (no memory)
+ */
+dbus_bool_t
+dbus_connection_allocate_data_slot (dbus_int32_t *slot_p)
+{
+ return _dbus_data_slot_allocator_alloc (&slot_allocator,
+ slot_p);
+}
+
+/**
+ * Deallocates a global ID for connection data slots.
+ * dbus_connection_get_data() and dbus_connection_set_data() may no
+ * longer be used with this slot. Existing data stored on existing
+ * DBusConnection objects will be freed when the connection is
+ * finalized, but may not be retrieved (and may only be replaced if
+ * someone else reallocates the slot). When the refcount on the
+ * passed-in slot reaches 0, it is set to -1.
+ *
+ * @param slot_p address storing the slot to deallocate
+ */
+void
+dbus_connection_free_data_slot (dbus_int32_t *slot_p)
+{
+ _dbus_return_if_fail (*slot_p >= 0);
+
+ _dbus_data_slot_allocator_free (&slot_allocator, slot_p);
+}
+
+/**
+ * Stores a pointer on a DBusConnection, along
+ * with an optional function to be used for freeing
+ * the data when the data is set again, or when
+ * the connection is finalized. The slot number
+ * must have been allocated with dbus_connection_allocate_data_slot().
+ *
+ * @note This function does not take the
+ * main thread lock on DBusConnection, which allows it to be
+ * used from inside watch and timeout functions. (See the
+ * note in docs for dbus_connection_set_watch_functions().)
+ * A side effect of this is that you need to know there's
+ * a reference held on the connection while invoking
+ * dbus_connection_set_data(), or the connection could be
+ * finalized during dbus_connection_set_data().
+ *
+ * @param connection the connection
+ * @param slot the slot number
+ * @param data the data to store
+ * @param free_data_func finalizer function for the data
+ * @returns #TRUE if there was enough memory to store the data
+ */
+dbus_bool_t
+dbus_connection_set_data (DBusConnection *connection,
+ dbus_int32_t slot,
+ void *data,
+ DBusFreeFunction free_data_func)
+{
+ DBusFreeFunction old_free_func;
+ void *old_data;
+ dbus_bool_t retval;
+
+ _dbus_return_val_if_fail (connection != NULL, FALSE);
+ _dbus_return_val_if_fail (slot >= 0, FALSE);
+
+ SLOTS_LOCK (connection);
+
+ retval = _dbus_data_slot_list_set (&slot_allocator,
+ &connection->slot_list,
+ slot, data, free_data_func,
+ &old_free_func, &old_data);
+
+ SLOTS_UNLOCK (connection);
+
+ if (retval)
+ {
+ /* Do the actual free outside the connection lock */
+ if (old_free_func)
+ (* old_free_func) (old_data);
+ }
+
+ return retval;
+}
+
+/**
+ * Retrieves data previously set with dbus_connection_set_data().
+ * The slot must still be allocated (must not have been freed).
+ *
+ * @note This function does not take the
+ * main thread lock on DBusConnection, which allows it to be
+ * used from inside watch and timeout functions. (See the
+ * note in docs for dbus_connection_set_watch_functions().)
+ * A side effect of this is that you need to know there's
+ * a reference held on the connection while invoking
+ * dbus_connection_get_data(), or the connection could be
+ * finalized during dbus_connection_get_data().
+ *
+ * @param connection the connection
+ * @param slot the slot to get data from
+ * @returns the data, or #NULL if not found
+ */
+void*
+dbus_connection_get_data (DBusConnection *connection,
+ dbus_int32_t slot)
+{
+ void *res;
+
+ _dbus_return_val_if_fail (connection != NULL, NULL);
+ _dbus_return_val_if_fail (slot >= 0, NULL);
+
+ SLOTS_LOCK (connection);
+
+ res = _dbus_data_slot_list_get (&slot_allocator,
+ &connection->slot_list,
+ slot);
+
+ SLOTS_UNLOCK (connection);
+
+ return res;
+}
+
+/**
+ * This function sets a global flag for whether dbus_connection_new()
+ * will set SIGPIPE behavior to SIG_IGN.
+ *
+ * @param will_modify_sigpipe #TRUE to allow sigpipe to be set to SIG_IGN
+ */
+void
+dbus_connection_set_change_sigpipe (dbus_bool_t will_modify_sigpipe)
+{
+ if (will_modify_sigpipe)
+ _dbus_atomic_set_nonzero (&_dbus_modify_sigpipe);
+ else
+ _dbus_atomic_set_zero (&_dbus_modify_sigpipe);
+}
+
+/**
+ * Specifies the maximum size message this connection is allowed to
+ * receive. Larger messages will result in disconnecting the
+ * connection.
+ *
+ * @param connection a #DBusConnection
+ * @param size maximum message size the connection can receive, in bytes
+ */
+void
+dbus_connection_set_max_message_size (DBusConnection *connection,
+ long size)
+{
+ _dbus_return_if_fail (connection != NULL);
+
+ CONNECTION_LOCK (connection);
+ _dbus_transport_set_max_message_size (connection->transport,
+ size);
+ CONNECTION_UNLOCK (connection);
+}
+
+/**
+ * Gets the value set by dbus_connection_set_max_message_size().
+ *
+ * @param connection the connection
+ * @returns the max size of a single message
+ */
+long
+dbus_connection_get_max_message_size (DBusConnection *connection)
+{
+ long res;
+
+ _dbus_return_val_if_fail (connection != NULL, 0);
+
+ CONNECTION_LOCK (connection);
+ res = _dbus_transport_get_max_message_size (connection->transport);
+ CONNECTION_UNLOCK (connection);
+ return res;
+}
+
+/**
+ * Specifies the maximum number of unix fds a message on this
+ * connection is allowed to receive. Messages with more unix fds will
+ * result in disconnecting the connection.
+ *
+ * @param connection a #DBusConnection
+ * @param n maximum message unix fds the connection can receive
+ */
+void
+dbus_connection_set_max_message_unix_fds (DBusConnection *connection,
+ long n)
+{
+ _dbus_return_if_fail (connection != NULL);
+
+ CONNECTION_LOCK (connection);
+ _dbus_transport_set_max_message_unix_fds (connection->transport,
+ n);
+ CONNECTION_UNLOCK (connection);
+}
+
+/**
+ * Gets the value set by dbus_connection_set_max_message_unix_fds().
+ *
+ * @param connection the connection
+ * @returns the max numer of unix fds of a single message
+ */
+long
+dbus_connection_get_max_message_unix_fds (DBusConnection *connection)
+{
+ long res;
+
+ _dbus_return_val_if_fail (connection != NULL, 0);
+
+ CONNECTION_LOCK (connection);
+ res = _dbus_transport_get_max_message_unix_fds (connection->transport);
+ CONNECTION_UNLOCK (connection);
+ return res;
+}
+
+/**
+ * Sets the maximum total number of bytes that can be used for all messages
+ * received on this connection. Messages count toward the maximum until
+ * they are finalized. When the maximum is reached, the connection will
+ * not read more data until some messages are finalized.
+ *
+ * The semantics of the maximum are: if outstanding messages are
+ * already above the maximum, additional messages will not be read.
+ * The semantics are not: if the next message would cause us to exceed
+ * the maximum, we don't read it. The reason is that we don't know the
+ * size of a message until after we read it.
+ *
+ * Thus, the max live messages size can actually be exceeded
+ * by up to the maximum size of a single message.
+ *
+ * Also, if we read say 1024 bytes off the wire in a single read(),
+ * and that contains a half-dozen small messages, we may exceed the
+ * size max by that amount. But this should be inconsequential.
+ *
+ * This does imply that we can't call read() with a buffer larger
+ * than we're willing to exceed this limit by.
+ *
+ * @param connection the connection
+ * @param size the maximum size in bytes of all outstanding messages
+ */
+void
+dbus_connection_set_max_received_size (DBusConnection *connection,
+ long size)
+{
+ _dbus_return_if_fail (connection != NULL);
+
+ CONNECTION_LOCK (connection);
+ _dbus_transport_set_max_received_size (connection->transport,
+ size);
+ CONNECTION_UNLOCK (connection);
+}
+
+/**
+ * Gets the value set by dbus_connection_set_max_received_size().
+ *
+ * @param connection the connection
+ * @returns the max size of all live messages
+ */
+long
+dbus_connection_get_max_received_size (DBusConnection *connection)
+{
+ long res;
+
+ _dbus_return_val_if_fail (connection != NULL, 0);
+
+ CONNECTION_LOCK (connection);
+ res = _dbus_transport_get_max_received_size (connection->transport);
+ CONNECTION_UNLOCK (connection);
+ return res;
+}
+
+/**
+ * Sets the maximum total number of unix fds that can be used for all messages
+ * received on this connection. Messages count toward the maximum until
+ * they are finalized. When the maximum is reached, the connection will
+ * not read more data until some messages are finalized.
+ *
+ * The semantics are analogous to those of dbus_connection_set_max_received_size().
+ *
+ * @param connection the connection
+ * @param n the maximum size in bytes of all outstanding messages
+ */
+void
+dbus_connection_set_max_received_unix_fds (DBusConnection *connection,
+ long n)
+{
+ _dbus_return_if_fail (connection != NULL);
+
+ CONNECTION_LOCK (connection);
+ _dbus_transport_set_max_received_unix_fds (connection->transport,
+ n);
+ CONNECTION_UNLOCK (connection);
+}
+
+/**
+ * Gets the value set by dbus_connection_set_max_received_unix_fds().
+ *
+ * @param connection the connection
+ * @returns the max unix fds of all live messages
+ */
+long
+dbus_connection_get_max_received_unix_fds (DBusConnection *connection)
+{
+ long res;
+
+ _dbus_return_val_if_fail (connection != NULL, 0);
+
+ CONNECTION_LOCK (connection);
+ res = _dbus_transport_get_max_received_unix_fds (connection->transport);
+ CONNECTION_UNLOCK (connection);
+ return res;
+}
+
+/**
+ * Gets the approximate size in bytes of all messages in the outgoing
+ * message queue. The size is approximate in that you shouldn't use
+ * it to decide how many bytes to read off the network or anything
+ * of that nature, as optimizations may choose to tell small white lies
+ * to avoid performance overhead.
+ *
+ * @param connection the connection
+ * @returns the number of bytes that have been queued up but not sent
+ */
+long
+dbus_connection_get_outgoing_size (DBusConnection *connection)
+{
+ long res;
+
+ _dbus_return_val_if_fail (connection != NULL, 0);
+
+ CONNECTION_LOCK (connection);
+ res = _dbus_counter_get_size_value (connection->outgoing_counter);
+ CONNECTION_UNLOCK (connection);
+ return res;
+}
+
+#ifdef DBUS_ENABLE_STATS
+void
+_dbus_connection_get_stats (DBusConnection *connection,
+ dbus_uint32_t *in_messages,
+ dbus_uint32_t *in_bytes,
+ dbus_uint32_t *in_fds,
+ dbus_uint32_t *in_peak_bytes,
+ dbus_uint32_t *in_peak_fds,
+ dbus_uint32_t *out_messages,
+ dbus_uint32_t *out_bytes,
+ dbus_uint32_t *out_fds,
+ dbus_uint32_t *out_peak_bytes,
+ dbus_uint32_t *out_peak_fds)
+{
+ CONNECTION_LOCK (connection);
+
+ if (in_messages != NULL)
+ *in_messages = connection->n_incoming;
+
+ _dbus_transport_get_stats (connection->transport,
+ in_bytes, in_fds, in_peak_bytes, in_peak_fds);
+
+ if (out_messages != NULL)
+ *out_messages = connection->n_outgoing;
+
+ if (out_bytes != NULL)
+ *out_bytes = _dbus_counter_get_size_value (connection->outgoing_counter);
+
+ if (out_fds != NULL)
+ *out_fds = _dbus_counter_get_unix_fd_value (connection->outgoing_counter);
+
+ if (out_peak_bytes != NULL)
+ *out_peak_bytes = _dbus_counter_get_peak_size_value (connection->outgoing_counter);
+
+ if (out_peak_fds != NULL)
+ *out_peak_fds = _dbus_counter_get_peak_unix_fd_value (connection->outgoing_counter);
+
+ CONNECTION_UNLOCK (connection);
+}
+#endif /* DBUS_ENABLE_STATS */
+
+/**
+ * Gets the approximate number of uni fds of all messages in the
+ * outgoing message queue.
+ *
+ * @param connection the connection
+ * @returns the number of unix fds that have been queued up but not sent
+ */
+long
+dbus_connection_get_outgoing_unix_fds (DBusConnection *connection)
+{
+ long res;
+
+ _dbus_return_val_if_fail (connection != NULL, 0);
+
+ CONNECTION_LOCK (connection);
+ res = _dbus_counter_get_unix_fd_value (connection->outgoing_counter);
+ CONNECTION_UNLOCK (connection);
+ return res;
+}
+
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
+/**
+ * Returns the address of the transport object of this connection
+ *
+ * @param connection the connection
+ * @returns the address string
+ */
+const char*
+_dbus_connection_get_address (DBusConnection *connection)
+{
+ return _dbus_transport_get_address (connection->transport);
+}
+#endif
+
+/** @} */
diff --git a/src/3rdparty/libdbus/dbus/dbus-connection.h b/src/3rdparty/libdbus/dbus/dbus-connection.h
new file mode 100644
index 00000000..b79fda83
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-connection.h
@@ -0,0 +1,531 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-connection.h DBusConnection object
+ *
+ * Copyright (C) 2002, 2003 Red Hat Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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
+ *
+ */
+#if !defined (DBUS_INSIDE_DBUS_H) && !defined (DBUS_COMPILATION)
+#error "Only <dbus/dbus.h> can be included directly, this file may disappear or change contents."
+#endif
+
+#ifndef DBUS_CONNECTION_H
+#define DBUS_CONNECTION_H
+
+#include <dbus/dbus-errors.h>
+#include <dbus/dbus-macros.h>
+#include <dbus/dbus-memory.h>
+#include <dbus/dbus-message.h>
+#include <dbus/dbus-shared.h>
+
+DBUS_BEGIN_DECLS
+
+/**
+ * @addtogroup DBusConnection
+ * @{
+ */
+
+/* documented in dbus-watch.c */
+typedef struct DBusWatch DBusWatch;
+/* documented in dbus-timeout.c */
+typedef struct DBusTimeout DBusTimeout;
+/** Opaque type representing preallocated resources so a message can be sent without further memory allocation. */
+typedef struct DBusPreallocatedSend DBusPreallocatedSend;
+/** Opaque type representing a method call that has not yet received a reply. */
+typedef struct DBusPendingCall DBusPendingCall;
+/** Opaque type representing a connection to a remote application and associated incoming/outgoing message queues. */
+typedef struct DBusConnection DBusConnection;
+/** Set of functions that must be implemented to handle messages sent to a particular object path. */
+typedef struct DBusObjectPathVTable DBusObjectPathVTable;
+
+/**
+ * Indicates the status of a #DBusWatch.
+ */
+typedef enum
+{
+ DBUS_WATCH_READABLE = 1 << 0, /**< As in POLLIN */
+ DBUS_WATCH_WRITABLE = 1 << 1, /**< As in POLLOUT */
+ DBUS_WATCH_ERROR = 1 << 2, /**< As in POLLERR (can't watch for
+ * this, but can be present in
+ * current state passed to
+ * dbus_watch_handle()).
+ */
+ DBUS_WATCH_HANGUP = 1 << 3 /**< As in POLLHUP (can't watch for
+ * it, but can be present in current
+ * state passed to
+ * dbus_watch_handle()).
+ */
+ /* Internal to libdbus, there is also _DBUS_WATCH_NVAL in dbus-watch.h */
+} DBusWatchFlags;
+
+/**
+ * Indicates the status of incoming data on a #DBusConnection. This determines whether
+ * dbus_connection_dispatch() needs to be called.
+ */
+typedef enum
+{
+ DBUS_DISPATCH_DATA_REMAINS, /**< There is more data to potentially convert to messages. */
+ DBUS_DISPATCH_COMPLETE, /**< All currently available data has been processed. */
+ DBUS_DISPATCH_NEED_MEMORY /**< More memory is needed to continue. */
+} DBusDispatchStatus;
+
+/** Called when libdbus needs a new watch to be monitored by the main
+ * loop. Returns #FALSE if it lacks enough memory to add the
+ * watch. Set by dbus_connection_set_watch_functions() or
+ * dbus_server_set_watch_functions().
+ */
+typedef dbus_bool_t (* DBusAddWatchFunction) (DBusWatch *watch,
+ void *data);
+/** Called when dbus_watch_get_enabled() may return a different value
+ * than it did before. Set by dbus_connection_set_watch_functions()
+ * or dbus_server_set_watch_functions().
+ */
+typedef void (* DBusWatchToggledFunction) (DBusWatch *watch,
+ void *data);
+/** Called when libdbus no longer needs a watch to be monitored by the
+ * main loop. Set by dbus_connection_set_watch_functions() or
+ * dbus_server_set_watch_functions().
+ */
+typedef void (* DBusRemoveWatchFunction) (DBusWatch *watch,
+ void *data);
+/** Called when libdbus needs a new timeout to be monitored by the main
+ * loop. Returns #FALSE if it lacks enough memory to add the
+ * watch. Set by dbus_connection_set_timeout_functions() or
+ * dbus_server_set_timeout_functions().
+ */
+typedef dbus_bool_t (* DBusAddTimeoutFunction) (DBusTimeout *timeout,
+ void *data);
+/** Called when dbus_timeout_get_enabled() may return a different
+ * value than it did before.
+ * Set by dbus_connection_set_timeout_functions() or
+ * dbus_server_set_timeout_functions().
+ */
+typedef void (* DBusTimeoutToggledFunction) (DBusTimeout *timeout,
+ void *data);
+/** Called when libdbus no longer needs a timeout to be monitored by the
+ * main loop. Set by dbus_connection_set_timeout_functions() or
+ * dbus_server_set_timeout_functions().
+ */
+typedef void (* DBusRemoveTimeoutFunction) (DBusTimeout *timeout,
+ void *data);
+/** Called when the return value of dbus_connection_get_dispatch_status()
+ * may have changed. Set with dbus_connection_set_dispatch_status_function().
+ */
+typedef void (* DBusDispatchStatusFunction) (DBusConnection *connection,
+ DBusDispatchStatus new_status,
+ void *data);
+/**
+ * Called when the main loop's thread should be notified that there's now work
+ * to do. Set with dbus_connection_set_wakeup_main_function().
+ */
+typedef void (* DBusWakeupMainFunction) (void *data);
+
+/**
+ * Called during authentication to check whether the given UNIX user
+ * ID is allowed to connect, if the client tried to auth as a UNIX
+ * user ID. Normally on Windows this would never happen. Set with
+ * dbus_connection_set_unix_user_function().
+ */
+typedef dbus_bool_t (* DBusAllowUnixUserFunction) (DBusConnection *connection,
+ unsigned long uid,
+ void *data);
+
+/**
+ * Called during authentication to check whether the given Windows user
+ * ID is allowed to connect, if the client tried to auth as a Windows
+ * user ID. Normally on UNIX this would never happen. Set with
+ * dbus_connection_set_windows_user_function().
+ */
+typedef dbus_bool_t (* DBusAllowWindowsUserFunction) (DBusConnection *connection,
+ const char *user_sid,
+ void *data);
+
+
+/**
+ * Called when a pending call now has a reply available. Set with
+ * dbus_pending_call_set_notify().
+ */
+typedef void (* DBusPendingCallNotifyFunction) (DBusPendingCall *pending,
+ void *user_data);
+
+/**
+ * Called when a message needs to be handled. The result indicates whether or
+ * not more handlers should be run. Set with dbus_connection_add_filter().
+ */
+typedef DBusHandlerResult (* DBusHandleMessageFunction) (DBusConnection *connection,
+ DBusMessage *message,
+ void *user_data);
+DBUS_EXPORT
+DBusConnection* dbus_connection_open (const char *address,
+ DBusError *error);
+DBUS_EXPORT
+DBusConnection* dbus_connection_open_private (const char *address,
+ DBusError *error);
+DBUS_EXPORT
+DBusConnection* dbus_connection_ref (DBusConnection *connection);
+DBUS_EXPORT
+void dbus_connection_unref (DBusConnection *connection);
+DBUS_EXPORT
+void dbus_connection_close (DBusConnection *connection);
+DBUS_EXPORT
+dbus_bool_t dbus_connection_get_is_connected (DBusConnection *connection);
+DBUS_EXPORT
+dbus_bool_t dbus_connection_get_is_authenticated (DBusConnection *connection);
+DBUS_EXPORT
+dbus_bool_t dbus_connection_get_is_anonymous (DBusConnection *connection);
+DBUS_EXPORT
+char* dbus_connection_get_server_id (DBusConnection *connection);
+DBUS_EXPORT
+dbus_bool_t dbus_connection_can_send_type (DBusConnection *connection,
+ int type);
+
+DBUS_EXPORT
+void dbus_connection_set_exit_on_disconnect (DBusConnection *connection,
+ dbus_bool_t exit_on_disconnect);
+DBUS_EXPORT
+void dbus_connection_flush (DBusConnection *connection);
+DBUS_EXPORT
+dbus_bool_t dbus_connection_read_write_dispatch (DBusConnection *connection,
+ int timeout_milliseconds);
+DBUS_EXPORT
+dbus_bool_t dbus_connection_read_write (DBusConnection *connection,
+ int timeout_milliseconds);
+DBUS_EXPORT
+DBusMessage* dbus_connection_borrow_message (DBusConnection *connection);
+DBUS_EXPORT
+void dbus_connection_return_message (DBusConnection *connection,
+ DBusMessage *message);
+DBUS_EXPORT
+void dbus_connection_steal_borrowed_message (DBusConnection *connection,
+ DBusMessage *message);
+DBUS_EXPORT
+DBusMessage* dbus_connection_pop_message (DBusConnection *connection);
+DBUS_EXPORT
+DBusDispatchStatus dbus_connection_get_dispatch_status (DBusConnection *connection);
+DBUS_EXPORT
+DBusDispatchStatus dbus_connection_dispatch (DBusConnection *connection);
+DBUS_EXPORT
+dbus_bool_t dbus_connection_has_messages_to_send (DBusConnection *connection);
+DBUS_EXPORT
+dbus_bool_t dbus_connection_send (DBusConnection *connection,
+ DBusMessage *message,
+ dbus_uint32_t *client_serial);
+DBUS_EXPORT
+dbus_bool_t dbus_connection_send_with_reply (DBusConnection *connection,
+ DBusMessage *message,
+ DBusPendingCall **pending_return,
+ int timeout_milliseconds);
+DBUS_EXPORT
+DBusMessage * dbus_connection_send_with_reply_and_block (DBusConnection *connection,
+ DBusMessage *message,
+ int timeout_milliseconds,
+ DBusError *error);
+DBUS_EXPORT
+dbus_bool_t dbus_connection_set_watch_functions (DBusConnection *connection,
+ DBusAddWatchFunction add_function,
+ DBusRemoveWatchFunction remove_function,
+ DBusWatchToggledFunction toggled_function,
+ void *data,
+ DBusFreeFunction free_data_function);
+DBUS_EXPORT
+dbus_bool_t dbus_connection_set_timeout_functions (DBusConnection *connection,
+ DBusAddTimeoutFunction add_function,
+ DBusRemoveTimeoutFunction remove_function,
+ DBusTimeoutToggledFunction toggled_function,
+ void *data,
+ DBusFreeFunction free_data_function);
+DBUS_EXPORT
+void dbus_connection_set_wakeup_main_function (DBusConnection *connection,
+ DBusWakeupMainFunction wakeup_main_function,
+ void *data,
+ DBusFreeFunction free_data_function);
+DBUS_EXPORT
+void dbus_connection_set_dispatch_status_function (DBusConnection *connection,
+ DBusDispatchStatusFunction function,
+ void *data,
+ DBusFreeFunction free_data_function);
+DBUS_EXPORT
+dbus_bool_t dbus_connection_get_unix_user (DBusConnection *connection,
+ unsigned long *uid);
+DBUS_EXPORT
+dbus_bool_t dbus_connection_get_unix_process_id (DBusConnection *connection,
+ unsigned long *pid);
+DBUS_EXPORT
+dbus_bool_t dbus_connection_get_adt_audit_session_data (DBusConnection *connection,
+ void **data,
+ dbus_int32_t *data_size);
+DBUS_EXPORT
+void dbus_connection_set_unix_user_function (DBusConnection *connection,
+ DBusAllowUnixUserFunction function,
+ void *data,
+ DBusFreeFunction free_data_function);
+DBUS_EXPORT
+dbus_bool_t dbus_connection_get_windows_user (DBusConnection *connection,
+ char **windows_sid_p);
+DBUS_EXPORT
+void dbus_connection_set_windows_user_function (DBusConnection *connection,
+ DBusAllowWindowsUserFunction function,
+ void *data,
+ DBusFreeFunction free_data_function);
+DBUS_EXPORT
+void dbus_connection_set_allow_anonymous (DBusConnection *connection,
+ dbus_bool_t value);
+DBUS_EXPORT
+void dbus_connection_set_builtin_filters_enabled (DBusConnection *connection,
+ dbus_bool_t value);
+DBUS_EXPORT
+void dbus_connection_set_route_peer_messages (DBusConnection *connection,
+ dbus_bool_t value);
+
+
+/* Filters */
+
+DBUS_EXPORT
+dbus_bool_t dbus_connection_add_filter (DBusConnection *connection,
+ DBusHandleMessageFunction function,
+ void *user_data,
+ DBusFreeFunction free_data_function);
+DBUS_EXPORT
+void dbus_connection_remove_filter (DBusConnection *connection,
+ DBusHandleMessageFunction function,
+ void *user_data);
+
+
+/* Other */
+DBUS_EXPORT
+dbus_bool_t dbus_connection_allocate_data_slot (dbus_int32_t *slot_p);
+DBUS_EXPORT
+void dbus_connection_free_data_slot (dbus_int32_t *slot_p);
+DBUS_EXPORT
+dbus_bool_t dbus_connection_set_data (DBusConnection *connection,
+ dbus_int32_t slot,
+ void *data,
+ DBusFreeFunction free_data_func);
+DBUS_EXPORT
+void* dbus_connection_get_data (DBusConnection *connection,
+ dbus_int32_t slot);
+
+DBUS_EXPORT
+void dbus_connection_set_change_sigpipe (dbus_bool_t will_modify_sigpipe);
+
+DBUS_EXPORT
+void dbus_connection_set_max_message_size (DBusConnection *connection,
+ long size);
+DBUS_EXPORT
+long dbus_connection_get_max_message_size (DBusConnection *connection);
+DBUS_EXPORT
+void dbus_connection_set_max_received_size (DBusConnection *connection,
+ long size);
+DBUS_EXPORT
+long dbus_connection_get_max_received_size (DBusConnection *connection);
+
+DBUS_EXPORT
+void dbus_connection_set_max_message_unix_fds (DBusConnection *connection,
+ long n);
+DBUS_EXPORT
+long dbus_connection_get_max_message_unix_fds (DBusConnection *connection);
+DBUS_EXPORT
+void dbus_connection_set_max_received_unix_fds(DBusConnection *connection,
+ long n);
+DBUS_EXPORT
+long dbus_connection_get_max_received_unix_fds(DBusConnection *connection);
+
+DBUS_EXPORT
+long dbus_connection_get_outgoing_size (DBusConnection *connection);
+DBUS_EXPORT
+long dbus_connection_get_outgoing_unix_fds (DBusConnection *connection);
+
+DBUS_EXPORT
+DBusPreallocatedSend* dbus_connection_preallocate_send (DBusConnection *connection);
+DBUS_EXPORT
+void dbus_connection_free_preallocated_send (DBusConnection *connection,
+ DBusPreallocatedSend *preallocated);
+DBUS_EXPORT
+void dbus_connection_send_preallocated (DBusConnection *connection,
+ DBusPreallocatedSend *preallocated,
+ DBusMessage *message,
+ dbus_uint32_t *client_serial);
+
+
+/* Object tree functionality */
+
+/**
+ * Called when a #DBusObjectPathVTable is unregistered (or its connection is freed).
+ * Found in #DBusObjectPathVTable.
+ */
+typedef void (* DBusObjectPathUnregisterFunction) (DBusConnection *connection,
+ void *user_data);
+/**
+ * Called when a message is sent to a registered object path. Found in
+ * #DBusObjectPathVTable which is registered with dbus_connection_register_object_path()
+ * or dbus_connection_register_fallback().
+ */
+typedef DBusHandlerResult (* DBusObjectPathMessageFunction) (DBusConnection *connection,
+ DBusMessage *message,
+ void *user_data);
+
+/**
+ * Virtual table that must be implemented to handle a portion of the
+ * object path hierarchy. Attach the vtable to a particular path using
+ * dbus_connection_register_object_path() or
+ * dbus_connection_register_fallback().
+ */
+struct DBusObjectPathVTable
+{
+ DBusObjectPathUnregisterFunction unregister_function; /**< Function to unregister this handler */
+ DBusObjectPathMessageFunction message_function; /**< Function to handle messages */
+
+ void (* dbus_internal_pad1) (void *); /**< Reserved for future expansion */
+ void (* dbus_internal_pad2) (void *); /**< Reserved for future expansion */
+ void (* dbus_internal_pad3) (void *); /**< Reserved for future expansion */
+ void (* dbus_internal_pad4) (void *); /**< Reserved for future expansion */
+};
+
+DBUS_EXPORT
+dbus_bool_t dbus_connection_try_register_object_path (DBusConnection *connection,
+ const char *path,
+ const DBusObjectPathVTable *vtable,
+ void *user_data,
+ DBusError *error);
+
+DBUS_EXPORT
+dbus_bool_t dbus_connection_register_object_path (DBusConnection *connection,
+ const char *path,
+ const DBusObjectPathVTable *vtable,
+ void *user_data);
+
+DBUS_EXPORT
+dbus_bool_t dbus_connection_try_register_fallback (DBusConnection *connection,
+ const char *path,
+ const DBusObjectPathVTable *vtable,
+ void *user_data,
+ DBusError *error);
+
+DBUS_EXPORT
+dbus_bool_t dbus_connection_register_fallback (DBusConnection *connection,
+ const char *path,
+ const DBusObjectPathVTable *vtable,
+ void *user_data);
+DBUS_EXPORT
+dbus_bool_t dbus_connection_unregister_object_path (DBusConnection *connection,
+ const char *path);
+
+DBUS_EXPORT
+dbus_bool_t dbus_connection_get_object_path_data (DBusConnection *connection,
+ const char *path,
+ void **data_p);
+
+DBUS_EXPORT
+dbus_bool_t dbus_connection_list_registered (DBusConnection *connection,
+ const char *parent_path,
+ char ***child_entries);
+
+DBUS_EXPORT
+dbus_bool_t dbus_connection_get_unix_fd (DBusConnection *connection,
+ int *fd);
+DBUS_EXPORT
+dbus_bool_t dbus_connection_get_socket (DBusConnection *connection,
+ int *fd);
+
+/**
+ * Clear a variable or struct member that contains a #DBusConnection.
+ * If it does not contain #NULL, the connection that was previously
+ * there is unreferenced with dbus_connection_unref().
+ *
+ * For example, this function and the similar functions for
+ * other reference-counted types can be used in code like this:
+ *
+ * @code
+ * DBusConnection *conn = NULL;
+ * struct { ...; DBusMessage *m; ... } *larger_structure = ...;
+ *
+ * ... code that might set conn or m to be non-NULL ...
+ *
+ * dbus_clear_connection (&conn);
+ * dbus_clear_message (&larger_structure->m);
+ * @endcode
+ *
+ * @param pointer_to_connection A pointer to a variable or struct member.
+ * pointer_to_connection must not be #NULL, but *pointer_to_connection
+ * may be #NULL.
+ */
+static inline void
+dbus_clear_connection (DBusConnection **pointer_to_connection)
+{
+ _dbus_clear_pointer_impl (DBusConnection, pointer_to_connection,
+ dbus_connection_unref);
+}
+
+/** @} */
+
+
+/**
+ * @addtogroup DBusWatch
+ * @{
+ */
+
+#ifndef DBUS_DISABLE_DEPRECATED
+DBUS_EXPORT
+DBUS_DEPRECATED int dbus_watch_get_fd (DBusWatch *watch);
+#endif
+
+DBUS_EXPORT
+int dbus_watch_get_unix_fd (DBusWatch *watch);
+DBUS_EXPORT
+int dbus_watch_get_socket (DBusWatch *watch);
+DBUS_EXPORT
+unsigned int dbus_watch_get_flags (DBusWatch *watch);
+DBUS_EXPORT
+void* dbus_watch_get_data (DBusWatch *watch);
+DBUS_EXPORT
+void dbus_watch_set_data (DBusWatch *watch,
+ void *data,
+ DBusFreeFunction free_data_function);
+DBUS_EXPORT
+dbus_bool_t dbus_watch_handle (DBusWatch *watch,
+ unsigned int flags);
+DBUS_EXPORT
+dbus_bool_t dbus_watch_get_enabled (DBusWatch *watch);
+
+/** @} */
+
+/**
+ * @addtogroup DBusTimeout
+ * @{
+ */
+
+DBUS_EXPORT
+int dbus_timeout_get_interval (DBusTimeout *timeout);
+DBUS_EXPORT
+void* dbus_timeout_get_data (DBusTimeout *timeout);
+DBUS_EXPORT
+void dbus_timeout_set_data (DBusTimeout *timeout,
+ void *data,
+ DBusFreeFunction free_data_function);
+DBUS_EXPORT
+dbus_bool_t dbus_timeout_handle (DBusTimeout *timeout);
+DBUS_EXPORT
+dbus_bool_t dbus_timeout_get_enabled (DBusTimeout *timeout);
+
+/** @} */
+
+DBUS_END_DECLS
+
+#endif /* DBUS_CONNECTION_H */
diff --git a/src/3rdparty/libdbus/dbus/dbus-credentials.c b/src/3rdparty/libdbus/dbus/dbus-credentials.c
new file mode 100644
index 00000000..44cd40bf
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-credentials.c
@@ -0,0 +1,835 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-credentials.c Credentials provable through authentication
+ *
+ * Copyright (C) 2007 Red Hat Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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 <stdlib.h>
+#include <string.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_SYS_SYSCALL_H
+#include <sys/syscall.h>
+#endif
+#include "dbus-credentials.h"
+#include "dbus-internals.h"
+#ifdef DBUS_UNIX
+#include "dbus-sysdeps-unix.h"
+#endif
+
+/**
+ * @defgroup DBusCredentials Credentials provable through authentication
+ * @ingroup DBusInternals
+ * @brief DBusCredentials object
+ *
+ * Credentials are what you have to prove you have in order to
+ * authenticate. The main credentials right now are a unix user
+ * account, a Windows user account, or a UNIX process ID.
+ */
+
+/**
+ * @defgroup DBusCredentialsInternals Credentials implementation details
+ * @ingroup DBusInternals
+ * @brief DBusCredentials implementation details
+ *
+ * Private details of credentials code.
+ *
+ * @{
+ */
+
+struct DBusCredentials {
+ int refcount;
+ dbus_uid_t unix_uid;
+ dbus_gid_t *unix_gids;
+ size_t n_unix_gids;
+ dbus_pid_t pid;
+ int pid_fd;
+ char *windows_sid;
+ char *linux_security_label;
+ void *adt_audit_data;
+ dbus_int32_t adt_audit_data_size;
+};
+
+/** @} */
+
+/**
+ * @addtogroup DBusCredentials
+ * @{
+ */
+
+/**
+ * Creates a new credentials object.
+ *
+ * @returns the new object or #NULL if no memory
+ */
+DBusCredentials*
+_dbus_credentials_new (void)
+{
+ DBusCredentials *creds;
+
+ creds = dbus_new (DBusCredentials, 1);
+ if (creds == NULL)
+ return NULL;
+
+ creds->refcount = 1;
+ creds->unix_uid = DBUS_UID_UNSET;
+ creds->unix_gids = NULL;
+ creds->n_unix_gids = 0;
+ creds->pid = DBUS_PID_UNSET;
+ creds->pid_fd = -1;
+ creds->windows_sid = NULL;
+ creds->linux_security_label = NULL;
+ creds->adt_audit_data = NULL;
+ creds->adt_audit_data_size = 0;
+
+ return creds;
+}
+
+/**
+ * Creates a new object with the most important credentials (user ID and process ID) from the current process.
+ * @returns the new object or #NULL if no memory
+ */
+DBusCredentials*
+_dbus_credentials_new_from_current_process (void)
+{
+ DBusCredentials *creds;
+
+ creds = _dbus_credentials_new ();
+ if (creds == NULL)
+ return NULL;
+
+ if (!_dbus_credentials_add_from_current_process (creds))
+ {
+ _dbus_credentials_unref (creds);
+ return NULL;
+ }
+
+ return creds;
+}
+
+/**
+ * Increment refcount on credentials.
+ *
+ * @param credentials the object
+ */
+void
+_dbus_credentials_ref (DBusCredentials *credentials)
+{
+ _dbus_assert (credentials->refcount > 0);
+ credentials->refcount += 1;
+}
+
+/**
+ * Decrement refcount on credentials.
+ *
+ * @param credentials the object
+ */
+void
+_dbus_credentials_unref (DBusCredentials *credentials)
+{
+ _dbus_assert (credentials->refcount > 0);
+
+ credentials->refcount -= 1;
+ if (credentials->refcount == 0)
+ {
+ dbus_free (credentials->unix_gids);
+ dbus_free (credentials->windows_sid);
+ dbus_free (credentials->linux_security_label);
+ dbus_free (credentials->adt_audit_data);
+#ifdef DBUS_UNIX
+ if (credentials->pid_fd >= 0)
+ {
+ close (credentials->pid_fd);
+ credentials->pid_fd = -1;
+ }
+#endif
+ dbus_free (credentials);
+ }
+}
+
+/**
+ * Add a UNIX process ID to the credentials. If the
+ * process ID FD is set, it will always take
+ * precendence when querying the PID of this
+ * credential.
+ *
+ * @param credentials the object
+ * @param pid the process ID
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_credentials_add_pid (DBusCredentials *credentials,
+ dbus_pid_t pid)
+{
+ credentials->pid = pid;
+ return TRUE;
+}
+
+/**
+ * Add a UNIX process ID FD to the credentials. The
+ * FD is now owned by the credentials object.
+ *
+ * @param credentials the object
+ * @param pid_fd the process ID FD
+ * @returns #FALSE if no memory
+ */
+#ifndef DBUS_UNIX
+_DBUS_GNUC_NORETURN
+#endif
+void
+_dbus_credentials_take_pid_fd (DBusCredentials *credentials,
+ int pid_fd)
+{
+#ifdef DBUS_UNIX
+ if (credentials->pid_fd >= 0)
+ close (credentials->pid_fd);
+ credentials->pid_fd = pid_fd;
+#else
+ _dbus_assert_not_reached ("pidfd never set on non-Unix");
+#endif
+}
+
+/**
+ * Add a UNIX user ID to the credentials.
+ *
+ * @param credentials the object
+ * @param uid the user ID
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_credentials_add_unix_uid(DBusCredentials *credentials,
+ dbus_uid_t uid)
+{
+ credentials->unix_uid = uid;
+ return TRUE;
+
+}
+
+static int
+cmp_gidp (const void *a_, const void *b_)
+{
+ const dbus_gid_t *a = a_;
+ const dbus_gid_t *b = b_;
+
+ if (*a < *b)
+ return -1;
+
+ if (*a > *b)
+ return 1;
+
+ return 0;
+}
+
+/**
+ * Add UNIX group IDs to the credentials, replacing any group IDs that
+ * might already have been present.
+ *
+ * @param credentials the object
+ * @param gids the group IDs, which will be freed by the DBusCredentials object
+ * @param n_gids the number of group IDs
+ */
+void
+_dbus_credentials_take_unix_gids (DBusCredentials *credentials,
+ dbus_gid_t *gids,
+ size_t n_gids)
+{
+ /* So we can compare arrays via a simple memcmp */
+ qsort (gids, n_gids, sizeof (dbus_gid_t), cmp_gidp);
+
+ dbus_free (credentials->unix_gids);
+ credentials->unix_gids = gids;
+ credentials->n_unix_gids = n_gids;
+}
+
+/**
+ * Get the Unix group IDs.
+ *
+ * @param credentials the object
+ * @param gids the group IDs, which will be freed by the DBusCredentials object
+ * @param n_gids the number of group IDs
+ */
+dbus_bool_t
+_dbus_credentials_get_unix_gids (DBusCredentials *credentials,
+ const dbus_gid_t **gids,
+ size_t *n_gids)
+{
+ if (gids != NULL)
+ *gids = credentials->unix_gids;
+
+ if (n_gids != NULL)
+ *n_gids = credentials->n_unix_gids;
+
+ return (credentials->unix_gids != NULL);
+}
+
+/**
+ * Add a Windows user SID to the credentials.
+ *
+ * @param credentials the object
+ * @param windows_sid the user SID
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_credentials_add_windows_sid (DBusCredentials *credentials,
+ const char *windows_sid)
+{
+ char *copy;
+
+ copy = _dbus_strdup (windows_sid);
+ if (copy == NULL)
+ return FALSE;
+
+ dbus_free (credentials->windows_sid);
+ credentials->windows_sid = copy;
+
+ return TRUE;
+}
+
+/**
+ * Add a Linux security label, as used by LSMs such as SELinux, Smack and
+ * AppArmor, to the credentials.
+ *
+ * @param credentials the object
+ * @param label the label
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_credentials_add_linux_security_label (DBusCredentials *credentials,
+ const char *label)
+{
+ char *copy;
+
+ copy = _dbus_strdup (label);
+ if (copy == NULL)
+ return FALSE;
+
+ dbus_free (credentials->linux_security_label);
+ credentials->linux_security_label = copy;
+
+ return TRUE;
+}
+
+/**
+ * Add ADT audit data to the credentials.
+ *
+ * @param credentials the object
+ * @param audit_data the audit data
+ * @param size the length of audit data
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_credentials_add_adt_audit_data (DBusCredentials *credentials,
+ void *audit_data,
+ dbus_int32_t size)
+{
+ void *copy;
+ copy = _dbus_memdup (audit_data, size);
+ if (copy == NULL)
+ return FALSE;
+
+ dbus_free (credentials->adt_audit_data);
+ credentials->adt_audit_data = copy;
+ credentials->adt_audit_data_size = size;
+
+ return TRUE;
+}
+
+/**
+ * Checks whether the given credential is present.
+ *
+ * @param credentials the object
+ * @param type the credential to check for
+ * @returns #TRUE if the credential is present
+ */
+dbus_bool_t
+_dbus_credentials_include (DBusCredentials *credentials,
+ DBusCredentialType type)
+{
+ switch (type)
+ {
+ case DBUS_CREDENTIAL_UNIX_PROCESS_ID:
+ return credentials->pid != DBUS_PID_UNSET ||
+ credentials->pid_fd >= 0;
+ case DBUS_CREDENTIAL_UNIX_PROCESS_FD:
+ return credentials->pid_fd >= 0;
+ case DBUS_CREDENTIAL_UNIX_USER_ID:
+ return credentials->unix_uid != DBUS_UID_UNSET;
+ case DBUS_CREDENTIAL_UNIX_GROUP_IDS:
+ return credentials->unix_gids != NULL;
+ case DBUS_CREDENTIAL_WINDOWS_SID:
+ return credentials->windows_sid != NULL;
+ case DBUS_CREDENTIAL_LINUX_SECURITY_LABEL:
+ return credentials->linux_security_label != NULL;
+ case DBUS_CREDENTIAL_ADT_AUDIT_DATA_ID:
+ return credentials->adt_audit_data != NULL;
+ default:
+ _dbus_assert_not_reached ("Unknown credential enum value");
+ return FALSE;
+ }
+}
+
+/**
+ * Gets the UNIX process ID in the credentials, or #DBUS_PID_UNSET if
+ * the credentials object doesn't contain a process ID.
+ * If the PID FD is set, it will first try to resolve from it, and
+ * only return the stored PID if that fails.
+ *
+ * @param credentials the object
+ * @returns UNIX process ID
+ */
+dbus_pid_t
+_dbus_credentials_get_pid (DBusCredentials *credentials)
+{
+#ifdef DBUS_UNIX
+ dbus_pid_t pid;
+
+ if (credentials->pid_fd >= 0)
+ {
+ pid = _dbus_resolve_pid_fd (credentials->pid_fd);
+ if (pid > 0)
+ return pid;
+ }
+#endif
+
+ return credentials->pid;
+}
+
+/**
+ * Gets the UNIX process ID FD in the credentials as obtained by 'safe'
+ * means (e.g.: Linux's SO_PEERPIDFD), or -1 if the credentials object
+ * doesn't contain a process ID FD. The file FD is owned by the credentials
+ * object and must not be closed by the caller.
+ *
+ * @param credentials the object
+ * @returns UNIX process ID FD
+ */
+int
+_dbus_credentials_get_pid_fd (DBusCredentials *credentials)
+{
+ return credentials->pid_fd;
+}
+
+/**
+ * Gets the UNIX user ID in the credentials, or #DBUS_UID_UNSET if
+ * the credentials object doesn't contain a user ID.
+ *
+ * @param credentials the object
+ * @returns UNIX user ID
+ */
+dbus_uid_t
+_dbus_credentials_get_unix_uid (DBusCredentials *credentials)
+{
+ return credentials->unix_uid;
+}
+
+/**
+ * Gets the Windows user SID in the credentials, or #NULL if
+ * the credentials object doesn't contain a Windows user SID.
+ *
+ * @param credentials the object
+ * @returns Windows user SID
+ */
+const char*
+_dbus_credentials_get_windows_sid (DBusCredentials *credentials)
+{
+ return credentials->windows_sid;
+}
+
+/**
+ * Gets the Linux security label (as used by LSMs) from the credentials,
+ * or #NULL if the credentials object doesn't contain a security label.
+ *
+ * @param credentials the object
+ * @returns the security label
+ */
+const char *
+_dbus_credentials_get_linux_security_label (DBusCredentials *credentials)
+{
+ return credentials->linux_security_label;
+}
+
+/**
+ * Gets the ADT audit data in the credentials, or #NULL if
+ * the credentials object doesn't contain ADT audit data.
+ *
+ * @param credentials the object
+ * @returns Solaris ADT audit data
+ */
+void *
+_dbus_credentials_get_adt_audit_data (DBusCredentials *credentials)
+{
+ return credentials->adt_audit_data;
+}
+
+/**
+ * Gets the ADT audit data size in the credentials, or 0 if
+ * the credentials object doesn't contain ADT audit data.
+ *
+ * @param credentials the object
+ * @returns Solaris ADT audit data size
+ */
+dbus_int32_t
+_dbus_credentials_get_adt_audit_data_size (DBusCredentials *credentials)
+{
+ return credentials->adt_audit_data_size;
+}
+
+/**
+ * Checks whether the first credentials object contains
+ * all the credentials found in the second credentials object.
+ *
+ * @param credentials the object
+ * @param possible_subset see if credentials in here are also in the first arg
+ * @returns #TRUE if second arg is contained in first
+ */
+dbus_bool_t
+_dbus_credentials_are_superset (DBusCredentials *credentials,
+ DBusCredentials *possible_subset)
+{
+ return
+ (possible_subset->pid == DBUS_PID_UNSET ||
+ possible_subset->pid == credentials->pid) &&
+ (possible_subset->unix_uid == DBUS_UID_UNSET ||
+ possible_subset->unix_uid == credentials->unix_uid) &&
+ (possible_subset->unix_gids == NULL ||
+ (possible_subset->n_unix_gids == credentials->n_unix_gids &&
+ memcmp (possible_subset->unix_gids, credentials->unix_gids,
+ sizeof (dbus_gid_t) * credentials->n_unix_gids) == 0)) &&
+ (possible_subset->windows_sid == NULL ||
+ (credentials->windows_sid && strcmp (possible_subset->windows_sid,
+ credentials->windows_sid) == 0)) &&
+ (possible_subset->linux_security_label == NULL ||
+ (credentials->linux_security_label != NULL &&
+ strcmp (possible_subset->linux_security_label,
+ credentials->linux_security_label) == 0)) &&
+ (possible_subset->adt_audit_data == NULL ||
+ (credentials->adt_audit_data && memcmp (possible_subset->adt_audit_data,
+ credentials->adt_audit_data,
+ credentials->adt_audit_data_size) == 0));
+}
+
+/**
+ * Checks whether a credentials object contains anything.
+ *
+ * @param credentials the object
+ * @returns #TRUE if there are no credentials in the object
+ */
+dbus_bool_t
+_dbus_credentials_are_empty (DBusCredentials *credentials)
+{
+ return
+ credentials->pid == DBUS_PID_UNSET &&
+ credentials->pid_fd == -1 &&
+ credentials->unix_uid == DBUS_UID_UNSET &&
+ credentials->unix_gids == NULL &&
+ credentials->n_unix_gids == 0 &&
+ credentials->windows_sid == NULL &&
+ credentials->linux_security_label == NULL &&
+ credentials->adt_audit_data == NULL;
+}
+
+/**
+ * Checks whether a credentials object contains a user identity.
+ *
+ * @param credentials the object
+ * @returns #TRUE if there are no user identities in the object
+ */
+dbus_bool_t
+_dbus_credentials_are_anonymous (DBusCredentials *credentials)
+{
+ return
+ credentials->unix_uid == DBUS_UID_UNSET &&
+ credentials->windows_sid == NULL;
+}
+
+/**
+ * Merge all credentials found in the second object into the first object,
+ * overwriting the first object if there are any overlaps.
+ *
+ * @param credentials the object
+ * @param other_credentials credentials to merge
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_credentials_add_credentials (DBusCredentials *credentials,
+ DBusCredentials *other_credentials)
+{
+ return
+ _dbus_credentials_add_credential (credentials,
+ DBUS_CREDENTIAL_UNIX_PROCESS_FD,
+ other_credentials) &&
+ _dbus_credentials_add_credential (credentials,
+ DBUS_CREDENTIAL_UNIX_PROCESS_ID,
+ other_credentials) &&
+ _dbus_credentials_add_credential (credentials,
+ DBUS_CREDENTIAL_UNIX_USER_ID,
+ other_credentials) &&
+ _dbus_credentials_add_credential (credentials,
+ DBUS_CREDENTIAL_UNIX_GROUP_IDS,
+ other_credentials) &&
+ _dbus_credentials_add_credential (credentials,
+ DBUS_CREDENTIAL_ADT_AUDIT_DATA_ID,
+ other_credentials) &&
+ _dbus_credentials_add_credential (credentials,
+ DBUS_CREDENTIAL_LINUX_SECURITY_LABEL,
+ other_credentials) &&
+ _dbus_credentials_add_credential (credentials,
+ DBUS_CREDENTIAL_WINDOWS_SID,
+ other_credentials);
+}
+
+/**
+ * Merge the given credential found in the second object into the first object,
+ * overwriting the first object's value for that credential.
+ *
+ * Does nothing if the second object does not contain the specified credential.
+ * i.e., will never delete a credential from the first object.
+ *
+ * @param credentials the object
+ * @param which the credential to overwrite
+ * @param other_credentials credentials to merge
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_credentials_add_credential (DBusCredentials *credentials,
+ DBusCredentialType which,
+ DBusCredentials *other_credentials)
+{
+ if (which == DBUS_CREDENTIAL_UNIX_PROCESS_ID &&
+ other_credentials->pid != DBUS_PID_UNSET)
+ {
+ if (!_dbus_credentials_add_pid (credentials, other_credentials->pid))
+ return FALSE;
+ }
+ else if (which == DBUS_CREDENTIAL_UNIX_USER_ID &&
+ other_credentials->unix_uid != DBUS_UID_UNSET)
+ {
+ if (!_dbus_credentials_add_unix_uid (credentials, other_credentials->unix_uid))
+ return FALSE;
+ }
+ else if (which == DBUS_CREDENTIAL_UNIX_GROUP_IDS &&
+ other_credentials->unix_gids != NULL)
+ {
+ dbus_gid_t *gids;
+
+ gids = dbus_new (dbus_gid_t, other_credentials->n_unix_gids);
+
+ if (gids == NULL)
+ return FALSE;
+
+ memcpy (gids, other_credentials->unix_gids,
+ sizeof (dbus_gid_t) * other_credentials->n_unix_gids);
+
+ _dbus_credentials_take_unix_gids (credentials, gids,
+ other_credentials->n_unix_gids);
+ }
+ else if (which == DBUS_CREDENTIAL_WINDOWS_SID &&
+ other_credentials->windows_sid != NULL)
+ {
+ if (!_dbus_credentials_add_windows_sid (credentials, other_credentials->windows_sid))
+ return FALSE;
+ }
+ else if (which == DBUS_CREDENTIAL_LINUX_SECURITY_LABEL &&
+ other_credentials->linux_security_label != NULL)
+ {
+ if (!_dbus_credentials_add_linux_security_label (credentials,
+ other_credentials->linux_security_label))
+ return FALSE;
+ }
+ else if (which == DBUS_CREDENTIAL_ADT_AUDIT_DATA_ID &&
+ other_credentials->adt_audit_data != NULL)
+ {
+ if (!_dbus_credentials_add_adt_audit_data (credentials, other_credentials->adt_audit_data, other_credentials->adt_audit_data_size))
+ return FALSE;
+ }
+ /* _dbus_dup() is only available on UNIX platforms. */
+#ifdef DBUS_UNIX
+ else if (which == DBUS_CREDENTIAL_UNIX_PROCESS_FD &&
+ other_credentials->pid_fd >= 0)
+ {
+ int pid_fd = _dbus_dup (other_credentials->pid_fd, NULL);
+
+ if (pid_fd < 0)
+ return FALSE;
+
+ _dbus_credentials_take_pid_fd (credentials, pid_fd);
+ }
+#endif
+
+ return TRUE;
+}
+
+/**
+ * Clear all credentials in the object.
+ *
+ * @param credentials the object
+ */
+void
+_dbus_credentials_clear (DBusCredentials *credentials)
+{
+ credentials->pid = DBUS_PID_UNSET;
+#ifdef DBUS_UNIX
+ if (credentials->pid_fd >= 0)
+ {
+ close (credentials->pid_fd);
+ credentials->pid_fd = -1;
+ }
+#endif
+ credentials->unix_uid = DBUS_UID_UNSET;
+ dbus_free (credentials->unix_gids);
+ credentials->unix_gids = NULL;
+ credentials->n_unix_gids = 0;
+ dbus_free (credentials->windows_sid);
+ credentials->windows_sid = NULL;
+ dbus_free (credentials->linux_security_label);
+ credentials->linux_security_label = NULL;
+ dbus_free (credentials->adt_audit_data);
+ credentials->adt_audit_data = NULL;
+ credentials->adt_audit_data_size = 0;
+}
+
+/**
+ * Copy a credentials object.
+ *
+ * @param credentials the object
+ * @returns the copy or #NULL
+ */
+DBusCredentials*
+_dbus_credentials_copy (DBusCredentials *credentials)
+{
+ DBusCredentials *copy;
+
+ copy = _dbus_credentials_new ();
+ if (copy == NULL)
+ return NULL;
+
+ if (!_dbus_credentials_add_credentials (copy, credentials))
+ {
+ _dbus_credentials_unref (copy);
+ return NULL;
+ }
+
+ return copy;
+}
+
+/**
+ * Check whether the user-identifying credentials in two credentials
+ * objects are identical. Credentials that are not related to the
+ * user are ignored, but any kind of user ID credentials must be the
+ * same (UNIX user ID, Windows user SID, etc.) and present in both
+ * objects for the function to return #TRUE.
+ *
+ * @param credentials the object
+ * @param other_credentials credentials to compare
+ * @returns #TRUE if the two credentials refer to the same user
+ */
+dbus_bool_t
+_dbus_credentials_same_user (DBusCredentials *credentials,
+ DBusCredentials *other_credentials)
+{
+ /* both windows and unix user must be the same (though pretty much
+ * in all conceivable cases, one will be unset)
+ */
+ return credentials->unix_uid == other_credentials->unix_uid &&
+ ((!(credentials->windows_sid || other_credentials->windows_sid)) ||
+ (credentials->windows_sid && other_credentials->windows_sid &&
+ strcmp (credentials->windows_sid, other_credentials->windows_sid) == 0));
+}
+
+/**
+ * Convert the credentials in this object to a human-readable
+ * string format, and append to the given string.
+ *
+ * @param credentials the object
+ * @param string append to this string
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_credentials_to_string_append (DBusCredentials *credentials,
+ DBusString *string)
+{
+ dbus_bool_t join;
+
+ join = FALSE;
+ if (credentials->unix_uid != DBUS_UID_UNSET)
+ {
+ if (!_dbus_string_append_printf (string, "uid=" DBUS_UID_FORMAT, credentials->unix_uid))
+ goto oom;
+ join = TRUE;
+ }
+ if (credentials->pid != DBUS_PID_UNSET || credentials->pid_fd >= 0)
+ {
+ if (!_dbus_string_append_printf (string,
+ "%spid=" DBUS_PID_FORMAT,
+ join ? " " : "",
+ _dbus_credentials_get_pid (credentials)))
+ goto oom;
+ join = TRUE;
+ }
+
+ if (credentials->unix_gids != NULL)
+ {
+ size_t i;
+
+ for (i = 0; i < credentials->n_unix_gids; i++)
+ {
+ if (!_dbus_string_append_printf (string, "%sgid=" DBUS_GID_FORMAT,
+ join ? " " : "",
+ credentials->unix_gids[i]))
+ goto oom;
+
+ join = TRUE;
+ }
+ }
+
+ if (credentials->windows_sid != NULL)
+ {
+ if (!_dbus_string_append_printf (string, "%ssid=%s", join ? " " : "", credentials->windows_sid))
+ goto oom;
+ join = TRUE;
+ }
+
+ if (credentials->linux_security_label != NULL)
+ {
+ if (!_dbus_string_append_printf (string, "%slsm='%s'",
+ join ? " " : "",
+ credentials->linux_security_label))
+ goto oom;
+ join = TRUE;
+ }
+
+ if (credentials->pid_fd >= 0)
+ {
+ if (!_dbus_string_append_printf (string, "%spidfd=%d", join ? " " : "", credentials->pid_fd))
+ goto oom;
+ join = TRUE;
+ }
+
+ return TRUE;
+oom:
+ return FALSE;
+}
+
+/** @} */
+
+/* tests in dbus-credentials-util.c */
diff --git a/src/3rdparty/libdbus/dbus/dbus-credentials.h b/src/3rdparty/libdbus/dbus/dbus-credentials.h
new file mode 100644
index 00000000..407d5cb5
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-credentials.h
@@ -0,0 +1,126 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-credentials.h Credentials provable through authentication
+ *
+ * Copyright (C) 2007 Red Hat Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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
+ *
+ */
+#ifndef DBUS_CREDENTIALS_H
+#define DBUS_CREDENTIALS_H
+
+#include <dbus/dbus-macros.h>
+#include <dbus/dbus-errors.h>
+#include <dbus/dbus-string.h>
+#include <dbus/dbus-sysdeps.h>
+
+DBUS_BEGIN_DECLS
+
+typedef enum {
+ DBUS_CREDENTIAL_UNIX_PROCESS_ID,
+ DBUS_CREDENTIAL_UNIX_USER_ID,
+ DBUS_CREDENTIAL_UNIX_GROUP_IDS,
+ DBUS_CREDENTIAL_ADT_AUDIT_DATA_ID,
+ DBUS_CREDENTIAL_LINUX_SECURITY_LABEL,
+ DBUS_CREDENTIAL_WINDOWS_SID,
+ DBUS_CREDENTIAL_UNIX_PROCESS_FD,
+} DBusCredentialType;
+
+DBUS_PRIVATE_EXPORT
+DBusCredentials* _dbus_credentials_new_from_current_process (void);
+DBUS_PRIVATE_EXPORT
+DBusCredentials* _dbus_credentials_new (void);
+DBUS_PRIVATE_EXPORT
+void _dbus_credentials_ref (DBusCredentials *credentials);
+DBUS_PRIVATE_EXPORT
+void _dbus_credentials_unref (DBusCredentials *credentials);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_credentials_add_pid (DBusCredentials *credentials,
+ dbus_pid_t pid);
+DBUS_PRIVATE_EXPORT
+void _dbus_credentials_take_pid_fd (DBusCredentials *credentials,
+ int pid_fd);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_credentials_add_unix_uid (DBusCredentials *credentials,
+ dbus_uid_t uid);
+DBUS_PRIVATE_EXPORT
+void _dbus_credentials_take_unix_gids (DBusCredentials *credentials,
+ dbus_gid_t *gids,
+ size_t n_gids);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_credentials_add_windows_sid (DBusCredentials *credentials,
+ const char *windows_sid);
+dbus_bool_t _dbus_credentials_add_linux_security_label (DBusCredentials *credentials,
+ const char *label);
+dbus_bool_t _dbus_credentials_add_adt_audit_data (DBusCredentials *credentials,
+ void *audit_data,
+ dbus_int32_t size);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_credentials_include (DBusCredentials *credentials,
+ DBusCredentialType type);
+DBUS_PRIVATE_EXPORT
+dbus_pid_t _dbus_credentials_get_pid (DBusCredentials *credentials);
+DBUS_PRIVATE_EXPORT
+int _dbus_credentials_get_pid_fd (DBusCredentials *credentials);
+DBUS_PRIVATE_EXPORT
+dbus_uid_t _dbus_credentials_get_unix_uid (DBusCredentials *credentials);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_credentials_get_unix_gids (DBusCredentials *credentials,
+ const dbus_gid_t **gids,
+ size_t *n_gids);
+DBUS_PRIVATE_EXPORT
+const char* _dbus_credentials_get_windows_sid (DBusCredentials *credentials);
+DBUS_PRIVATE_EXPORT
+const char * _dbus_credentials_get_linux_security_label (DBusCredentials *credentials);
+void * _dbus_credentials_get_adt_audit_data (DBusCredentials *credentials);
+dbus_int32_t _dbus_credentials_get_adt_audit_data_size (DBusCredentials *credentials);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_credentials_are_superset (DBusCredentials *credentials,
+ DBusCredentials *possible_subset);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_credentials_are_empty (DBusCredentials *credentials);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_credentials_are_anonymous (DBusCredentials *credentials);
+dbus_bool_t _dbus_credentials_add_credentials (DBusCredentials *credentials,
+ DBusCredentials *other_credentials);
+/* must silently allow 'which' to not exist */
+dbus_bool_t _dbus_credentials_add_credential (DBusCredentials *credentials,
+ DBusCredentialType which,
+ DBusCredentials *other_credentials);
+DBUS_PRIVATE_EXPORT
+void _dbus_credentials_clear (DBusCredentials *credentials);
+DBUS_PRIVATE_EXPORT
+DBusCredentials* _dbus_credentials_copy (DBusCredentials *credentials);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_credentials_same_user (DBusCredentials *credentials,
+ DBusCredentials *other_credentials);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_credentials_to_string_append (DBusCredentials *credentials,
+ DBusString *string);
+
+static inline void
+_dbus_clear_credentials (DBusCredentials **pointer_to_creds)
+{
+ _dbus_clear_pointer_impl (DBusCredentials, pointer_to_creds,
+ _dbus_credentials_unref);
+}
+
+DBUS_END_DECLS
+
+#endif /* DBUS_CREDENTIALS_H */
diff --git a/src/3rdparty/libdbus/dbus/dbus-dataslot.c b/src/3rdparty/libdbus/dbus/dbus-dataslot.c
new file mode 100644
index 00000000..776f9278
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-dataslot.c
@@ -0,0 +1,466 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-dataslot.c storing data on objects
+ *
+ * Copyright (C) 2003 Red Hat, Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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-dataslot.h"
+#include "dbus-threads-internal.h"
+#include <dbus/dbus-test-tap.h>
+
+/**
+ * @defgroup DBusDataSlot Data slots
+ * @ingroup DBusInternals
+ * @brief Storing data by ID
+ *
+ * Types and functions related to storing data by an
+ * allocated ID. This is used for dbus_connection_set_data(),
+ * dbus_server_set_data(), etc.
+ * @{
+ */
+
+/**
+ * Initializes a data slot allocator object, used to assign
+ * integer IDs for data slots.
+ *
+ * @param allocator the allocator to initialize
+ */
+dbus_bool_t
+_dbus_data_slot_allocator_init (DBusDataSlotAllocator *allocator,
+ DBusGlobalLock lock)
+{
+ allocator->allocated_slots = NULL;
+ allocator->n_allocated_slots = 0;
+ allocator->n_used_slots = 0;
+ allocator->lock = lock;
+
+ return TRUE;
+}
+
+/**
+ * Allocates an integer ID to be used for storing data
+ * in a #DBusDataSlotList. If the value at *slot_id_p is
+ * not -1, this function just increments the refcount for
+ * the existing slot ID. If the value is -1, a new slot ID
+ * is allocated and stored at *slot_id_p.
+ *
+ * @param allocator the allocator
+ * @param slot_id_p address to fill with the slot ID
+ * @returns #TRUE on success
+ */
+dbus_bool_t
+_dbus_data_slot_allocator_alloc (DBusDataSlotAllocator *allocator,
+ dbus_int32_t *slot_id_p)
+{
+ dbus_int32_t slot;
+
+ if (!_dbus_lock (allocator->lock))
+ return FALSE;
+
+ if (*slot_id_p >= 0)
+ {
+ slot = *slot_id_p;
+
+ _dbus_assert (slot < allocator->n_allocated_slots);
+ _dbus_assert (allocator->allocated_slots[slot].slot_id == slot);
+
+ allocator->allocated_slots[slot].refcount += 1;
+
+ goto out;
+ }
+
+ _dbus_assert (*slot_id_p < 0);
+
+ if (allocator->n_used_slots < allocator->n_allocated_slots)
+ {
+ slot = 0;
+ while (slot < allocator->n_allocated_slots)
+ {
+ if (allocator->allocated_slots[slot].slot_id < 0)
+ {
+ allocator->allocated_slots[slot].slot_id = slot;
+ allocator->allocated_slots[slot].refcount = 1;
+ allocator->n_used_slots += 1;
+ break;
+ }
+ ++slot;
+ }
+
+ _dbus_assert (slot < allocator->n_allocated_slots);
+ }
+ else
+ {
+ DBusAllocatedSlot *tmp;
+
+ slot = -1;
+ tmp = dbus_realloc (allocator->allocated_slots,
+ sizeof (DBusAllocatedSlot) * (allocator->n_allocated_slots + 1));
+ if (tmp == NULL)
+ goto out;
+
+ allocator->allocated_slots = tmp;
+ slot = allocator->n_allocated_slots;
+ allocator->n_allocated_slots += 1;
+ allocator->n_used_slots += 1;
+ allocator->allocated_slots[slot].slot_id = slot;
+ allocator->allocated_slots[slot].refcount = 1;
+ }
+
+ _dbus_assert (slot >= 0);
+ _dbus_assert (slot < allocator->n_allocated_slots);
+ _dbus_assert (*slot_id_p < 0);
+ _dbus_assert (allocator->allocated_slots[slot].slot_id == slot);
+ _dbus_assert (allocator->allocated_slots[slot].refcount == 1);
+
+ *slot_id_p = slot;
+
+ _dbus_verbose ("Allocated slot %d on allocator %p total %d slots allocated %d used\n",
+ slot, allocator, allocator->n_allocated_slots, allocator->n_used_slots);
+
+ out:
+ _dbus_unlock (allocator->lock);
+ return slot >= 0;
+}
+
+/**
+ * Deallocates an ID previously allocated with
+ * _dbus_data_slot_allocator_alloc(). Existing data stored on
+ * existing #DBusDataSlotList objects with this ID will be freed when the
+ * data list is finalized, but may not be retrieved (and may only be
+ * replaced if someone else reallocates the slot).
+ * The slot value is reset to -1 if this is the last unref.
+ *
+ * @param allocator the allocator
+ * @param slot_id_p address where we store the slot
+ */
+void
+_dbus_data_slot_allocator_free (DBusDataSlotAllocator *allocator,
+ dbus_int32_t *slot_id_p)
+{
+ if (!_dbus_lock (allocator->lock))
+ _dbus_assert_not_reached ("we should have initialized global locks "
+ "before we allocated this slot");
+
+ _dbus_assert (*slot_id_p < allocator->n_allocated_slots);
+ _dbus_assert (allocator->allocated_slots[*slot_id_p].slot_id == *slot_id_p);
+ _dbus_assert (allocator->allocated_slots[*slot_id_p].refcount > 0);
+
+ allocator->allocated_slots[*slot_id_p].refcount -= 1;
+
+ if (allocator->allocated_slots[*slot_id_p].refcount > 0)
+ {
+ _dbus_unlock (allocator->lock);
+ return;
+ }
+
+ /* refcount is 0, free the slot */
+ _dbus_verbose ("Freeing slot %d on allocator %p total %d allocated %d used\n",
+ *slot_id_p, allocator, allocator->n_allocated_slots, allocator->n_used_slots);
+
+ allocator->allocated_slots[*slot_id_p].slot_id = -1;
+ *slot_id_p = -1;
+
+ allocator->n_used_slots -= 1;
+
+ if (allocator->n_used_slots == 0)
+ {
+ dbus_free (allocator->allocated_slots);
+ allocator->allocated_slots = NULL;
+ allocator->n_allocated_slots = 0;
+ }
+
+ _dbus_unlock (allocator->lock);
+}
+
+/**
+ * Initializes a slot list.
+ * @param list the list to initialize.
+ */
+void
+_dbus_data_slot_list_init (DBusDataSlotList *list)
+{
+ list->slots = NULL;
+ list->n_slots = 0;
+}
+
+/**
+ * Stores a pointer in the data slot list, along with an optional
+ * function to be used for freeing the data when the data is set
+ * again, or when the slot list is finalized. The slot number must
+ * have been allocated with _dbus_data_slot_allocator_alloc() for the
+ * same allocator passed in here. The same allocator has to be used
+ * with the slot list every time.
+ *
+ * @param allocator the allocator to use
+ * @param list the data slot list
+ * @param slot the slot number
+ * @param data the data to store
+ * @param free_data_func finalizer function for the data
+ * @param old_free_func free function for any previously-existing data
+ * @param old_data previously-existing data, should be freed with old_free_func
+ * @returns #TRUE if there was enough memory to store the data
+ */
+dbus_bool_t
+_dbus_data_slot_list_set (DBusDataSlotAllocator *allocator,
+ DBusDataSlotList *list,
+ int slot,
+ void *data,
+ DBusFreeFunction free_data_func,
+ DBusFreeFunction *old_free_func,
+ void **old_data)
+{
+#ifndef DBUS_DISABLE_ASSERT
+ /* We need to take the allocator lock here, because the allocator could
+ * be e.g. realloc()ing allocated_slots. We avoid doing this if asserts
+ * are disabled, since then the asserts are empty.
+ */
+ if (!_dbus_lock (allocator->lock))
+ _dbus_assert_not_reached ("we should have initialized global locks "
+ "before we allocated this slot");
+
+ _dbus_assert (slot < allocator->n_allocated_slots);
+ _dbus_assert (allocator->allocated_slots[slot].slot_id == slot);
+ _dbus_unlock (allocator->lock);
+#endif
+
+ if (slot >= list->n_slots)
+ {
+ DBusDataSlot *tmp;
+ int i;
+
+ tmp = dbus_realloc (list->slots,
+ sizeof (DBusDataSlot) * (slot + 1));
+ if (tmp == NULL)
+ return FALSE;
+
+ list->slots = tmp;
+ i = list->n_slots;
+ list->n_slots = slot + 1;
+ while (i < list->n_slots)
+ {
+ list->slots[i].data = NULL;
+ list->slots[i].free_data_func = NULL;
+ ++i;
+ }
+ }
+
+ _dbus_assert (slot < list->n_slots);
+
+ *old_data = list->slots[slot].data;
+ *old_free_func = list->slots[slot].free_data_func;
+
+ list->slots[slot].data = data;
+ list->slots[slot].free_data_func = free_data_func;
+
+ return TRUE;
+}
+
+/**
+ * Retrieves data previously set with _dbus_data_slot_list_set_data().
+ * The slot must still be allocated (must not have been freed).
+ *
+ * @param allocator the allocator slot was allocated from
+ * @param list the data slot list
+ * @param slot the slot to get data from
+ * @returns the data, or #NULL if not found
+ */
+void*
+_dbus_data_slot_list_get (DBusDataSlotAllocator *allocator,
+ DBusDataSlotList *list,
+ int slot)
+{
+#ifndef DBUS_DISABLE_ASSERT
+ /* We need to take the allocator lock here, because the allocator could
+ * be e.g. realloc()ing allocated_slots. We avoid doing this if asserts
+ * are disabled, since then the asserts are empty.
+ */
+ if (!_dbus_lock (allocator->lock))
+ _dbus_assert_not_reached ("we should have initialized global locks "
+ "before we allocated this slot");
+
+ _dbus_assert (slot >= 0);
+ _dbus_assert (slot < allocator->n_allocated_slots);
+ _dbus_assert (allocator->allocated_slots[slot].slot_id == slot);
+ _dbus_unlock (allocator->lock);
+#endif
+
+ if (slot >= list->n_slots)
+ return NULL;
+ else
+ return list->slots[slot].data;
+}
+
+/**
+ * Frees all data slots contained in the list, calling
+ * application-provided free functions if they exist.
+ *
+ * @param list the list to clear
+ */
+void
+_dbus_data_slot_list_clear (DBusDataSlotList *list)
+{
+ int i;
+
+ i = 0;
+ while (i < list->n_slots)
+ {
+ if (list->slots[i].free_data_func)
+ (* list->slots[i].free_data_func) (list->slots[i].data);
+ list->slots[i].data = NULL;
+ list->slots[i].free_data_func = NULL;
+ ++i;
+ }
+}
+
+/**
+ * Frees the data slot list and all data slots contained
+ * in it, calling application-provided free functions
+ * if they exist.
+ *
+ * @param list the list to free
+ */
+void
+_dbus_data_slot_list_free (DBusDataSlotList *list)
+{
+ _dbus_data_slot_list_clear (list);
+
+ dbus_free (list->slots);
+ list->slots = NULL;
+ list->n_slots = 0;
+}
+
+/** @} */
+
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
+#include "dbus-test.h"
+#include <stdio.h>
+
+/* Test-only, does not need to be thread-safe */
+static int free_counter;
+
+static void
+test_free_slot_data_func (void *data)
+{
+ int i = _DBUS_POINTER_TO_INT (data);
+
+ _dbus_assert (free_counter == i);
+ ++free_counter;
+}
+
+/**
+ * Test function for data slots
+ */
+dbus_bool_t
+_dbus_data_slot_test (const char *test_data_dir _DBUS_GNUC_UNUSED)
+{
+ DBusDataSlotAllocator allocator;
+ DBusDataSlotList list;
+ int i;
+ DBusFreeFunction old_free_func;
+ void *old_data;
+
+ if (!_dbus_data_slot_allocator_init (&allocator, _DBUS_LOCK_server_slots))
+ _dbus_test_fatal ("no memory for allocator");
+
+ _dbus_data_slot_list_init (&list);
+
+#define N_SLOTS 100
+
+ i = 0;
+ while (i < N_SLOTS)
+ {
+ /* we don't really want apps to rely on this ordered
+ * allocation, but it simplifies things to rely on it
+ * here.
+ */
+ dbus_int32_t tmp = -1;
+
+ _dbus_data_slot_allocator_alloc (&allocator, &tmp);
+
+ if (tmp != i)
+ _dbus_test_fatal ("did not allocate slots in numeric order");
+
+ ++i;
+ }
+
+ i = 0;
+ while (i < N_SLOTS)
+ {
+ if (!_dbus_data_slot_list_set (&allocator, &list,
+ i,
+ _DBUS_INT_TO_POINTER (i),
+ test_free_slot_data_func,
+ &old_free_func, &old_data))
+ _dbus_test_fatal ("no memory to set data");
+
+ _dbus_assert (old_free_func == NULL);
+ _dbus_assert (old_data == NULL);
+
+ _dbus_assert (_dbus_data_slot_list_get (&allocator, &list, i) ==
+ _DBUS_INT_TO_POINTER (i));
+
+ ++i;
+ }
+
+ free_counter = 0;
+ i = 0;
+ while (i < N_SLOTS)
+ {
+ if (!_dbus_data_slot_list_set (&allocator, &list,
+ i,
+ _DBUS_INT_TO_POINTER (i),
+ test_free_slot_data_func,
+ &old_free_func, &old_data))
+ _dbus_test_fatal ("no memory to set data");
+
+ _dbus_assert (old_free_func == test_free_slot_data_func);
+ _dbus_assert (_DBUS_POINTER_TO_INT (old_data) == i);
+
+ (* old_free_func) (old_data);
+ _dbus_assert (i == (free_counter - 1));
+
+ _dbus_assert (_dbus_data_slot_list_get (&allocator, &list, i) ==
+ _DBUS_INT_TO_POINTER (i));
+
+ ++i;
+ }
+
+ free_counter = 0;
+ _dbus_data_slot_list_free (&list);
+
+ _dbus_assert (N_SLOTS == free_counter);
+
+ i = 0;
+ while (i < N_SLOTS)
+ {
+ dbus_int32_t tmp = i;
+
+ _dbus_data_slot_allocator_free (&allocator, &tmp);
+ _dbus_assert (tmp == -1);
+ ++i;
+ }
+
+ return TRUE;
+}
+
+#endif /* DBUS_ENABLE_EMBEDDED_TESTS */
diff --git a/src/3rdparty/libdbus/dbus/dbus-dataslot.h b/src/3rdparty/libdbus/dbus/dbus-dataslot.h
new file mode 100644
index 00000000..08322c1c
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-dataslot.h
@@ -0,0 +1,100 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-dataslot.h storing data on objects
+ *
+ * Copyright (C) 2003 Red Hat, Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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
+ *
+ */
+#ifndef DBUS_DATASLOT_H
+#define DBUS_DATASLOT_H
+
+#include <dbus/dbus-internals.h>
+
+DBUS_BEGIN_DECLS
+
+typedef struct DBusDataSlotAllocator DBusDataSlotAllocator;
+typedef struct DBusDataSlotList DBusDataSlotList;
+
+/** Opaque typedef for DBusDataSlot */
+typedef struct DBusDataSlot DBusDataSlot;
+/** DBusDataSlot is used to store application data on the connection */
+struct DBusDataSlot
+{
+ void *data; /**< The application data */
+ DBusFreeFunction free_data_func; /**< Free the application data */
+};
+
+typedef struct DBusAllocatedSlot DBusAllocatedSlot;
+
+/** An allocated slot for storing data
+ */
+struct DBusAllocatedSlot
+{
+ dbus_int32_t slot_id; /**< ID of this slot */
+ int refcount; /**< Number of uses of the slot */
+};
+
+/**
+ * An allocator that tracks a set of slot IDs.
+ */
+struct DBusDataSlotAllocator
+{
+ DBusAllocatedSlot *allocated_slots; /**< Allocated slots */
+ int n_allocated_slots; /**< number of slots malloc'd */
+ int n_used_slots; /**< number of slots used */
+ DBusGlobalLock lock; /**< index of thread lock */
+};
+
+#define _DBUS_DATA_SLOT_ALLOCATOR_INIT(x) { NULL, 0, 0, x }
+
+/**
+ * Data structure that stores the actual user data set at a given
+ * slot.
+ */
+struct DBusDataSlotList
+{
+ DBusDataSlot *slots; /**< Data slots */
+ int n_slots; /**< Slots we have storage for in data_slots */
+};
+
+dbus_bool_t _dbus_data_slot_allocator_init (DBusDataSlotAllocator *allocator,
+ DBusGlobalLock lock);
+dbus_bool_t _dbus_data_slot_allocator_alloc (DBusDataSlotAllocator *allocator,
+ int *slot_id_p);
+void _dbus_data_slot_allocator_free (DBusDataSlotAllocator *allocator,
+ int *slot_id_p);
+void _dbus_data_slot_list_init (DBusDataSlotList *list);
+dbus_bool_t _dbus_data_slot_list_set (DBusDataSlotAllocator *allocator,
+ DBusDataSlotList *list,
+ int slot,
+ void *data,
+ DBusFreeFunction free_data_func,
+ DBusFreeFunction *old_free_func,
+ void **old_data);
+void* _dbus_data_slot_list_get (DBusDataSlotAllocator *allocator,
+ DBusDataSlotList *list,
+ int slot);
+void _dbus_data_slot_list_clear (DBusDataSlotList *list);
+void _dbus_data_slot_list_free (DBusDataSlotList *list);
+
+
+DBUS_END_DECLS
+
+#endif /* DBUS_DATASLOT_H */
diff --git a/src/3rdparty/libdbus/dbus/dbus-errors.c b/src/3rdparty/libdbus/dbus/dbus-errors.c
new file mode 100644
index 00000000..24e54a82
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-errors.c
@@ -0,0 +1,437 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-errors.c Error reporting
+ *
+ * Copyright (C) 2002, 2004 Red Hat Inc.
+ * Copyright (C) 2003 CodeFactory AB
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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-errors.h"
+#include "dbus-internals.h"
+#include "dbus-string.h"
+#include "dbus-protocol.h"
+#include <stdarg.h>
+#include <string.h>
+
+/**
+ * @defgroup DBusErrorInternals Error reporting internals
+ * @ingroup DBusInternals
+ * @brief Error reporting internals
+ * @{
+ */
+
+/**
+ * @def DBUS_ERROR_INIT
+ *
+ * Expands to a suitable initializer for a DBusError on the stack.
+ * Declaring a DBusError with:
+ *
+ * @code
+ * DBusError error = DBUS_ERROR_INIT;
+ *
+ * do_things_with (&error);
+ * @endcode
+ *
+ * is a more concise form of:
+ *
+ * @code
+ * DBusError error;
+ *
+ * dbus_error_init (&error);
+ * do_things_with (&error);
+ * @endcode
+ */
+
+/**
+ * Internals of DBusError
+ */
+typedef struct
+{
+ char *name; /**< error name */
+ char *message; /**< error message */
+
+ unsigned int const_message : 1; /**< Message is not owned by DBusError */
+
+ unsigned int dummy2 : 1; /**< placeholder */
+ unsigned int dummy3 : 1; /**< placeholder */
+ unsigned int dummy4 : 1; /**< placeholder */
+ unsigned int dummy5 : 1; /**< placeholder */
+
+ void *padding1; /**< placeholder */
+
+} DBusRealError;
+
+_DBUS_STATIC_ASSERT (sizeof (DBusRealError) == sizeof (DBusError));
+
+/**
+ * Returns a longer message describing an error name.
+ * If the error name is unknown, returns the name
+ * itself.
+ *
+ * @param error the error to describe
+ * @returns a constant string describing the error.
+ */
+static const char*
+message_from_error (const char *error)
+{
+ if (strcmp (error, DBUS_ERROR_FAILED) == 0)
+ return "Unknown error";
+ else if (strcmp (error, DBUS_ERROR_NO_MEMORY) == 0)
+ return "Not enough memory available";
+ else if (strcmp (error, DBUS_ERROR_IO_ERROR) == 0)
+ return "Error reading or writing data";
+ else if (strcmp (error, DBUS_ERROR_BAD_ADDRESS) == 0)
+ return "Could not parse address";
+ else if (strcmp (error, DBUS_ERROR_NOT_SUPPORTED) == 0)
+ return "Feature not supported";
+ else if (strcmp (error, DBUS_ERROR_LIMITS_EXCEEDED) == 0)
+ return "Resource limits exceeded";
+ else if (strcmp (error, DBUS_ERROR_ACCESS_DENIED) == 0)
+ return "Permission denied";
+ else if (strcmp (error, DBUS_ERROR_AUTH_FAILED) == 0)
+ return "Could not authenticate to server";
+ else if (strcmp (error, DBUS_ERROR_NO_SERVER) == 0)
+ return "No server available at address";
+ else if (strcmp (error, DBUS_ERROR_TIMEOUT) == 0)
+ return "Connection timed out";
+ else if (strcmp (error, DBUS_ERROR_NO_NETWORK) == 0)
+ return "Network unavailable";
+ else if (strcmp (error, DBUS_ERROR_ADDRESS_IN_USE) == 0)
+ return "Address already in use";
+ else if (strcmp (error, DBUS_ERROR_DISCONNECTED) == 0)
+ return "Disconnected.";
+ else if (strcmp (error, DBUS_ERROR_INVALID_ARGS) == 0)
+ return "Invalid arguments.";
+ else if (strcmp (error, DBUS_ERROR_NO_REPLY) == 0)
+ return "Did not get a reply message.";
+ else if (strcmp (error, DBUS_ERROR_FILE_NOT_FOUND) == 0)
+ return "File doesn't exist.";
+ else if (strcmp (error, DBUS_ERROR_OBJECT_PATH_IN_USE) == 0)
+ return "Object path already in use";
+ else
+ return error;
+}
+
+/** @} */ /* End of internals */
+
+/**
+ * @defgroup DBusErrors Error reporting
+ * @ingroup DBus
+ * @brief Error reporting
+ *
+ * Types and functions related to reporting errors.
+ *
+ *
+ * In essence D-Bus error reporting works as follows:
+ *
+ * @code
+ * DBusError error;
+ * dbus_error_init (&error);
+ * dbus_some_function (arg1, arg2, &error);
+ * if (dbus_error_is_set (&error))
+ * {
+ * fprintf (stderr, "an error occurred: %s\n", error.message);
+ * dbus_error_free (&error);
+ * }
+ * @endcode
+ *
+ * By convention, all functions allow #NULL instead of a DBusError*,
+ * so callers who don't care about the error can ignore it.
+ *
+ * There are some rules. An error passed to a D-Bus function must
+ * always be unset; you can't pass in an error that's already set. If
+ * a function has a return code indicating whether an error occurred,
+ * and also a #DBusError parameter, then the error will always be set
+ * if and only if the return code indicates an error occurred. i.e.
+ * the return code and the error are never going to disagree.
+ *
+ * An error only needs to be freed if it's been set, not if
+ * it's merely been initialized.
+ *
+ * You can check the specific error that occurred using
+ * dbus_error_has_name().
+ *
+ * Errors will not be set for programming errors, such as passing
+ * invalid arguments to the libdbus API. Instead, libdbus will print
+ * warnings, exit on a failed assertion, or even crash in those cases
+ * (in other words, incorrect use of the API results in undefined
+ * behavior, possibly accompanied by helpful debugging output if
+ * you're lucky).
+ *
+ * @{
+ */
+
+/**
+ * Initializes a DBusError structure. Does not allocate any memory;
+ * the error only needs to be freed if it is set at some point.
+ *
+ * @param error the DBusError.
+ */
+void
+dbus_error_init (DBusError *error)
+{
+ DBusRealError *real;
+
+ _DBUS_STATIC_ASSERT (sizeof (DBusError) == sizeof (DBusRealError));
+
+ _dbus_return_if_fail (error != NULL);
+
+ real = (DBusRealError *)error;
+
+ real->name = NULL;
+ real->message = NULL;
+
+ real->const_message = TRUE;
+}
+
+/**
+ * Frees an error that's been set (or just initialized),
+ * then reinitializes the error as in dbus_error_init().
+ *
+ * @param error memory where the error is stored.
+ */
+void
+dbus_error_free (DBusError *error)
+{
+ DBusRealError *real;
+
+ _dbus_return_if_fail (error != NULL);
+
+ real = (DBusRealError *)error;
+
+ if (!real->const_message)
+ {
+ dbus_free (real->name);
+ dbus_free (real->message);
+ }
+
+ dbus_error_init (error);
+}
+
+/**
+ * Assigns an error name and message to a DBusError. Does nothing if
+ * error is #NULL. The message may be #NULL, which means a default
+ * message will be deduced from the name. The default message will be
+ * totally useless, though, so using a #NULL message is not recommended.
+ *
+ * Because this function does not copy the error name or message, you
+ * must ensure the name and message are global data that won't be
+ * freed. You probably want dbus_set_error() instead, in most cases.
+ *
+ * @param error the error or #NULL
+ * @param name the error name (not copied!!!)
+ * @param message the error message (not copied!!!)
+ */
+void
+dbus_set_error_const (DBusError *error,
+ const char *name,
+ const char *message)
+{
+ DBusRealError *real;
+
+ _dbus_return_if_error_is_set (error);
+ _dbus_return_if_fail (name != NULL);
+
+ if (error == NULL)
+ return;
+
+ _dbus_assert (error->name == NULL);
+ _dbus_assert (error->message == NULL);
+
+ if (message == NULL)
+ message = message_from_error (name);
+
+ real = (DBusRealError *)error;
+
+ real->name = (char*) name;
+ real->message = (char *)message;
+ real->const_message = TRUE;
+}
+
+/**
+ * Moves an error src into dest, freeing src and
+ * overwriting dest. Both src and dest must be initialized.
+ * src is reinitialized to an empty error. dest may not
+ * contain an existing error. If the destination is
+ * #NULL, just frees and reinits the source error.
+ *
+ * @param src the source error
+ * @param dest the destination error or #NULL
+ */
+void
+dbus_move_error (DBusError *src,
+ DBusError *dest)
+{
+ _dbus_return_if_error_is_set (dest);
+
+ if (dest)
+ {
+ dbus_error_free (dest);
+ *dest = *src;
+ dbus_error_init (src);
+ }
+ else
+ dbus_error_free (src);
+}
+
+/**
+ * Checks whether the error is set and has the given
+ * name.
+ * @param error the error
+ * @param name the name
+ * @returns #TRUE if the given named error occurred
+ */
+dbus_bool_t
+dbus_error_has_name (const DBusError *error,
+ const char *name)
+{
+ _dbus_return_val_if_fail (error != NULL, FALSE);
+ _dbus_return_val_if_fail (name != NULL, FALSE);
+
+ _dbus_assert ((error->name != NULL && error->message != NULL) ||
+ (error->name == NULL && error->message == NULL));
+
+ if (error->name != NULL)
+ {
+ DBusString str1, str2;
+ _dbus_string_init_const (&str1, error->name);
+ _dbus_string_init_const (&str2, name);
+ return _dbus_string_equal (&str1, &str2);
+ }
+ else
+ return FALSE;
+}
+
+/**
+ * Checks whether an error occurred (the error is set).
+ *
+ * @param error the error object
+ * @returns #TRUE if an error occurred
+ */
+dbus_bool_t
+dbus_error_is_set (const DBusError *error)
+{
+ _dbus_return_val_if_fail (error != NULL, FALSE);
+ _dbus_assert ((error->name != NULL && error->message != NULL) ||
+ (error->name == NULL && error->message == NULL));
+ return error->name != NULL;
+}
+
+/**
+ * Assigns an error name and message to a DBusError.
+ * Does nothing if error is #NULL.
+ *
+ * The format may be #NULL, which means a (pretty much useless)
+ * default message will be deduced from the name. This is not a good
+ * idea, just go ahead and provide a useful error message. It won't
+ * hurt you.
+ *
+ * If no memory can be allocated for the error message,
+ * an out-of-memory error message will be set instead.
+ *
+ * @param error the error.or #NULL
+ * @param name the error name
+ * @param format printf-style format string.
+ */
+void
+dbus_set_error (DBusError *error,
+ const char *name,
+ const char *format,
+ ...)
+{
+ va_list args;
+
+ if (error == NULL)
+ return;
+
+ /* it's a bug to pile up errors */
+ _dbus_return_if_error_is_set (error);
+ _dbus_return_if_fail (name != NULL);
+
+ va_start (args, format);
+ _dbus_set_error_valist (error, name, format, args);
+ va_end (args);
+}
+
+void
+_dbus_set_error_valist (DBusError *error,
+ const char *name,
+ const char *format,
+ va_list args)
+{
+ DBusRealError *real;
+ DBusString str;
+
+ _dbus_assert (name != NULL);
+
+ if (error == NULL)
+ return;
+
+ _dbus_assert (error->name == NULL);
+ _dbus_assert (error->message == NULL);
+
+ if (!_dbus_string_init (&str))
+ goto nomem;
+
+ if (format == NULL)
+ {
+ if (!_dbus_string_append (&str,
+ message_from_error (name)))
+ {
+ _dbus_string_free (&str);
+ goto nomem;
+ }
+ }
+ else
+ {
+ if (!_dbus_string_append_printf_valist (&str, format, args))
+ {
+ _dbus_string_free (&str);
+ goto nomem;
+ }
+ }
+
+ real = (DBusRealError *)error;
+
+ if (!_dbus_string_steal_data (&str, &real->message))
+ {
+ _dbus_string_free (&str);
+ goto nomem;
+ }
+ _dbus_string_free (&str);
+
+ real->name = _dbus_strdup (name);
+ if (real->name == NULL)
+ {
+ dbus_free (real->message);
+ real->message = NULL;
+ goto nomem;
+ }
+ real->const_message = FALSE;
+
+ return;
+
+ nomem:
+ _DBUS_SET_OOM (error);
+}
+
+/** @} */ /* End public API */
diff --git a/src/3rdparty/libdbus/dbus/dbus-errors.h b/src/3rdparty/libdbus/dbus/dbus-errors.h
new file mode 100644
index 00000000..003f583b
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-errors.h
@@ -0,0 +1,92 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-errors.h Error reporting
+ *
+ * Copyright (C) 2002 Red Hat Inc.
+ * Copyright (C) 2003 CodeFactory AB
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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
+ *
+ */
+#if !defined (DBUS_INSIDE_DBUS_H) && !defined (DBUS_COMPILATION)
+#error "Only <dbus/dbus.h> can be included directly, this file may disappear or change contents."
+#endif
+
+#ifndef DBUS_ERROR_H
+#define DBUS_ERROR_H
+
+#include <dbus/dbus-macros.h>
+#include <dbus/dbus-types.h>
+#include <dbus/dbus-protocol.h>
+
+DBUS_BEGIN_DECLS
+
+/**
+ * @addtogroup DBusErrors
+ * @{
+ */
+
+/** Mostly-opaque type representing an error that occurred */
+typedef struct DBusError DBusError;
+
+/**
+ * Object representing an exception.
+ */
+struct DBusError
+{
+ const char *name; /**< public error name field */
+ const char *message; /**< public error message field */
+
+ unsigned int dummy1 : 1; /**< placeholder */
+ unsigned int dummy2 : 1; /**< placeholder */
+ unsigned int dummy3 : 1; /**< placeholder */
+ unsigned int dummy4 : 1; /**< placeholder */
+ unsigned int dummy5 : 1; /**< placeholder */
+
+ void *padding1; /**< placeholder */
+};
+
+#define DBUS_ERROR_INIT { NULL, NULL, TRUE, 0, 0, 0, 0, NULL }
+
+DBUS_EXPORT
+void dbus_error_init (DBusError *error);
+DBUS_EXPORT
+void dbus_error_free (DBusError *error);
+DBUS_EXPORT
+void dbus_set_error (DBusError *error,
+ const char *name,
+ const char *message,
+ ...) _DBUS_GNUC_PRINTF (3, 4);
+DBUS_EXPORT
+void dbus_set_error_const (DBusError *error,
+ const char *name,
+ const char *message);
+DBUS_EXPORT
+void dbus_move_error (DBusError *src,
+ DBusError *dest);
+DBUS_EXPORT
+dbus_bool_t dbus_error_has_name (const DBusError *error,
+ const char *name);
+DBUS_EXPORT
+dbus_bool_t dbus_error_is_set (const DBusError *error);
+
+/** @} */
+
+DBUS_END_DECLS
+
+#endif /* DBUS_ERROR_H */
diff --git a/src/3rdparty/libdbus/dbus/dbus-file-unix.c b/src/3rdparty/libdbus/dbus/dbus-file-unix.c
new file mode 100644
index 00000000..d5fa029e
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-file-unix.c
@@ -0,0 +1,459 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-file-unix.c unix related file implementation (internal to D-Bus implementation)
+ *
+ * Copyright (C) 2002, 2003, 2006 Red Hat, Inc.
+ * Copyright (C) 2003 CodeFactory AB
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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-protocol.h"
+#include "dbus-errors.h"
+#include "dbus-file.h"
+#include "dbus-internals.h"
+#include "dbus-sysdeps.h"
+#include "dbus-sysdeps-unix.h"
+
+#ifdef HAVE_LINUX_MAGIC_H
+#include <linux/magic.h>
+#endif
+#include <sys/stat.h>
+#ifdef HAVE_SYS_VFS_H
+#include <sys/vfs.h>
+#endif
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+/**
+ * Appends the contents of the given file to the string,
+ * returning error code. At the moment, won't open a file
+ * more than a megabyte in size.
+ *
+ * @param str the string to append to
+ * @param filename filename to load
+ * @param error place to set an error
+ * @returns #FALSE if error was set
+ */
+dbus_bool_t
+_dbus_file_get_contents (DBusString *str,
+ const DBusString *filename,
+ DBusError *error)
+{
+ int fd;
+ struct stat sb;
+#ifdef HAVE_FSTATFS
+ struct statfs sfs;
+#endif
+ int orig_len;
+ int total;
+ int file_size;
+ const char *filename_c;
+ dbus_bool_t is_procfs = FALSE;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ filename_c = _dbus_string_get_const_data (filename);
+
+ /* O_BINARY useful on Cygwin */
+ fd = open (filename_c, O_RDONLY | O_BINARY);
+ if (fd < 0)
+ {
+ dbus_set_error (error, _dbus_error_from_errno (errno),
+ "Failed to open \"%s\": %s",
+ filename_c,
+ _dbus_strerror (errno));
+ return FALSE;
+ }
+
+ _dbus_verbose ("file fd %d opened\n", fd);
+
+ if (fstat (fd, &sb) < 0)
+ {
+ dbus_set_error (error, _dbus_error_from_errno (errno),
+ "Failed to stat \"%s\": %s",
+ filename_c,
+ _dbus_strerror (errno));
+
+ _dbus_verbose ("fstat() failed: %s",
+ _dbus_strerror (errno));
+
+ _dbus_close (fd, NULL);
+
+ return FALSE;
+ }
+
+ if (sb.st_size > _DBUS_ONE_MEGABYTE)
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "File size %lu of \"%s\" is too large.",
+ (unsigned long) sb.st_size, filename_c);
+ _dbus_close (fd, NULL);
+ return FALSE;
+ }
+
+ /* procfs has different semantics - most files are 0 size,
+ * we can do only one read, and at most we can read 4M.
+ */
+#ifdef HAVE_FSTATFS
+ if (sb.st_size == 0)
+ {
+ if (fstatfs(fd, &sfs) < 0)
+ {
+ dbus_set_error (error, _dbus_error_from_errno (errno),
+ "Failed to stat \"%s\": %s",
+ filename_c,
+ _dbus_strerror (errno));
+
+ _dbus_verbose ("fstatvfs() failed: %s",
+ _dbus_strerror (errno));
+
+ _dbus_close (fd, NULL);
+
+ return FALSE;
+ }
+ if (sfs.f_type == PROC_SUPER_MAGIC)
+ is_procfs = TRUE;
+ }
+#endif
+
+ if (is_procfs)
+ file_size = _DBUS_ONE_MEGABYTE;
+ else
+ file_size = sb.st_size;
+
+ total = 0;
+ orig_len = _dbus_string_get_length (str);
+ if (file_size > 0 && S_ISREG (sb.st_mode))
+ {
+ int bytes_read;
+
+ do
+ {
+ bytes_read = _dbus_read (fd, str,
+ file_size - total);
+ if (bytes_read <= 0)
+ {
+ dbus_set_error (error, _dbus_error_from_errno (errno),
+ "Error reading \"%s\": %s",
+ filename_c,
+ _dbus_strerror (errno));
+
+ _dbus_verbose ("read() failed: %s",
+ _dbus_strerror (errno));
+
+ _dbus_close (fd, NULL);
+ _dbus_string_set_length (str, orig_len);
+ return FALSE;
+ }
+ else
+ total += bytes_read;
+ }
+ while (total < file_size && !is_procfs);
+
+ _dbus_close (fd, NULL);
+ return TRUE;
+ }
+ else if (file_size != 0)
+ {
+ _dbus_verbose ("Can only open regular files at the moment.\n");
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "\"%s\" is not a regular file",
+ filename_c);
+ _dbus_close (fd, NULL);
+ return FALSE;
+ }
+ else
+ {
+ _dbus_close (fd, NULL);
+ return TRUE;
+ }
+}
+
+/**
+ * Writes a string out to a file. If the file exists,
+ * it will be atomically overwritten by the new data.
+ *
+ * @param str the string to write out
+ * @param filename the file to save string to
+ * @param world_readable If set, ensure the file is world readable
+ * @param error error to be filled in on failure
+ * @returns #FALSE on failure
+ */
+dbus_bool_t
+_dbus_string_save_to_file (const DBusString *str,
+ const DBusString *filename,
+ dbus_bool_t world_readable,
+ DBusError *error)
+{
+ int fd;
+ int bytes_to_write;
+ const char *filename_c;
+ DBusString tmp_filename;
+ const char *tmp_filename_c;
+ int total;
+ dbus_bool_t need_unlink;
+ dbus_bool_t retval;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ fd = -1;
+ retval = FALSE;
+ need_unlink = FALSE;
+
+ if (!_dbus_string_init (&tmp_filename))
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ return FALSE;
+ }
+
+ if (!_dbus_string_copy (filename, 0, &tmp_filename, 0))
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ _dbus_string_free (&tmp_filename);
+ return FALSE;
+ }
+
+ if (!_dbus_string_append (&tmp_filename, "."))
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ _dbus_string_free (&tmp_filename);
+ return FALSE;
+ }
+
+#define N_TMP_FILENAME_RANDOM_BYTES 8
+ if (!_dbus_generate_random_ascii (&tmp_filename, N_TMP_FILENAME_RANDOM_BYTES,
+ error))
+ {
+ _dbus_string_free (&tmp_filename);
+ return FALSE;
+ }
+
+ filename_c = _dbus_string_get_const_data (filename);
+ tmp_filename_c = _dbus_string_get_const_data (&tmp_filename);
+
+ fd = open (tmp_filename_c, O_WRONLY | O_BINARY | O_EXCL | O_CREAT,
+ world_readable ? 0644 : 0600);
+ if (fd < 0)
+ {
+ dbus_set_error (error, _dbus_error_from_errno (errno),
+ "Could not create %s: %s", tmp_filename_c,
+ _dbus_strerror (errno));
+ goto out;
+ }
+ if (world_readable)
+ {
+ /* Ensure the file is world readable even in the presence of
+ * possibly restrictive umasks;
+ * see http://lists.freedesktop.org/archives/dbus/2010-September/013367.html
+ */
+ if (fchmod (fd, 0644) < 0)
+ {
+ dbus_set_error (error, _dbus_error_from_errno (errno),
+ "Could not chmod %s: %s", tmp_filename_c,
+ _dbus_strerror (errno));
+ goto out;
+ }
+ }
+
+ _dbus_verbose ("tmp file fd %d opened\n", fd);
+
+ need_unlink = TRUE;
+
+ total = 0;
+ bytes_to_write = _dbus_string_get_length (str);
+
+ while (total < bytes_to_write)
+ {
+ int bytes_written;
+
+ bytes_written = _dbus_write (fd, str, total,
+ bytes_to_write - total);
+
+ if (bytes_written <= 0)
+ {
+ dbus_set_error (error, _dbus_error_from_errno (errno),
+ "Could not write to %s: %s", tmp_filename_c,
+ _dbus_strerror (errno));
+
+ goto out;
+ }
+
+ total += bytes_written;
+ }
+
+ if (fsync(fd))
+ {
+ dbus_set_error (error, _dbus_error_from_errno (errno),
+ "Could not synchronize file %s: %s",
+ tmp_filename_c, _dbus_strerror (errno));
+
+ goto out;
+ }
+
+ if (!_dbus_close (fd, NULL))
+ {
+ dbus_set_error (error, _dbus_error_from_errno (errno),
+ "Could not close file %s: %s",
+ tmp_filename_c, _dbus_strerror (errno));
+
+ goto out;
+ }
+
+ fd = -1;
+
+ if (rename (tmp_filename_c, filename_c) < 0)
+ {
+ dbus_set_error (error, _dbus_error_from_errno (errno),
+ "Could not rename %s to %s: %s",
+ tmp_filename_c, filename_c,
+ _dbus_strerror (errno));
+
+ goto out;
+ }
+
+ need_unlink = FALSE;
+
+ retval = TRUE;
+
+ out:
+ /* close first, then unlink, to prevent ".nfs34234235" garbage
+ * files
+ */
+
+ if (fd >= 0)
+ _dbus_close (fd, NULL);
+
+ if (need_unlink && unlink (tmp_filename_c) < 0)
+ _dbus_verbose ("Failed to unlink temp file %s: %s\n",
+ tmp_filename_c, _dbus_strerror (errno));
+
+ _dbus_string_free (&tmp_filename);
+
+ _DBUS_ASSERT_ERROR_XOR_BOOL (error, retval);
+ return retval;
+}
+
+/** Makes the file readable by every user in the system.
+ *
+ * @param filename the filename
+ * @param error error location
+ * @returns #TRUE if the file's permissions could be changed.
+ */
+dbus_bool_t
+_dbus_make_file_world_readable(const DBusString *filename,
+ DBusError *error)
+{
+ const char *filename_c;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ filename_c = _dbus_string_get_const_data (filename);
+ if (chmod (filename_c, 0644) == -1)
+ {
+ dbus_set_error (error,
+ DBUS_ERROR_FAILED,
+ "Could not change permissions of file %s: %s\n",
+ filename_c,
+ _dbus_strerror (errno));
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/** Creates the given file, failing if the file already exists.
+ *
+ * @param filename the filename
+ * @param error error location
+ * @returns #TRUE if we created the file and it didn't exist
+ */
+dbus_bool_t
+_dbus_create_file_exclusively (const DBusString *filename,
+ DBusError *error)
+{
+ int fd;
+ const char *filename_c;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ filename_c = _dbus_string_get_const_data (filename);
+
+ fd = open (filename_c, O_WRONLY | O_BINARY | O_EXCL | O_CREAT,
+ 0600);
+ if (fd < 0)
+ {
+ dbus_set_error (error,
+ DBUS_ERROR_FAILED,
+ "Could not create file %s: %s\n",
+ filename_c,
+ _dbus_strerror (errno));
+ return FALSE;
+ }
+
+ _dbus_verbose ("exclusive file fd %d opened\n", fd);
+
+ if (!_dbus_close (fd, NULL))
+ {
+ dbus_set_error (error,
+ DBUS_ERROR_FAILED,
+ "Could not close file %s: %s\n",
+ filename_c,
+ _dbus_strerror (errno));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**
+ * Deletes the given file.
+ *
+ * @param filename the filename
+ * @param error error location
+ *
+ * @returns #TRUE if unlink() succeeded
+ */
+dbus_bool_t
+_dbus_delete_file (const DBusString *filename,
+ DBusError *error)
+{
+ const char *filename_c;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ filename_c = _dbus_string_get_const_data (filename);
+
+ if (unlink (filename_c) < 0)
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "Failed to delete file %s: %s\n",
+ filename_c, _dbus_strerror (errno));
+ return FALSE;
+ }
+ else
+ return TRUE;
+}
diff --git a/src/3rdparty/libdbus/dbus/dbus-file-win.c b/src/3rdparty/libdbus/dbus/dbus-file-win.c
new file mode 100644
index 00000000..a17e0869
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-file-win.c
@@ -0,0 +1,408 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-file-win.c windows related file implementation (internal to D-Bus implementation)
+ *
+ * Copyright (C) 2002, 2003, 2006 Red Hat, Inc.
+ * Copyright (C) 2003 CodeFactory AB
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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-protocol.h"
+#include "dbus-string.h"
+#include "dbus-internals.h"
+#include "dbus-sysdeps-win.h"
+#include "dbus-pipe.h"
+
+#include <windows.h>
+
+
+/**
+ * Thin wrapper around the read() system call that appends
+ * the data it reads to the DBusString buffer. It appends
+ * up to the given count.
+ *
+ * @param hnd the HANDLE to read from
+ * @param buffer the buffer to append data to
+ * @param count the amount of data to read
+ * @param error place to set an error
+ * @returns the number of bytes read or -1
+ */
+static int
+_dbus_file_read (HANDLE hnd,
+ DBusString *buffer,
+ int count,
+ DBusError *error)
+{
+ BOOL result;
+ DWORD bytes_read;
+ int start;
+ char *data;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ _dbus_assert (count >= 0);
+
+ start = _dbus_string_get_length (buffer);
+
+ if (!_dbus_string_lengthen (buffer, count))
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ return -1;
+ }
+
+ data = _dbus_string_get_data_len (buffer, start, count);
+
+ result = ReadFile (hnd, data, count, &bytes_read, NULL);
+ if (result == 0)
+ {
+ char *emsg = _dbus_win_error_string (GetLastError ());
+ dbus_set_error (error, _dbus_win_error_from_last_error (),
+ "Failed to read from %p: %s", hnd, emsg);
+ _dbus_win_free_error_string (emsg);
+ return -1;
+ }
+
+ if (bytes_read)
+ {
+ /* put length back (doesn't actually realloc) */
+ _dbus_string_set_length (buffer, start + bytes_read);
+
+#if 0
+ if (bytes_read > 0)
+ _dbus_verbose_bytes_of_string (buffer, start, bytes_read);
+#endif
+ }
+
+ return bytes_read;
+}
+
+
+/**
+ * Appends the contents of the given file to the string,
+ * returning error code. At the moment, won't open a file
+ * more than a megabyte in size.
+ *
+ * @param str the string to append to
+ * @param filename filename to load
+ * @param error place to set an error
+ * @returns #FALSE if error was set
+ */
+dbus_bool_t
+_dbus_file_get_contents (DBusString *str,
+ const DBusString *filename,
+ DBusError *error)
+{
+ HANDLE hnd;
+ DWORD fsize;
+ DWORD fsize_hi;
+ int orig_len;
+ unsigned int total;
+ const char *filename_c;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ filename_c = _dbus_string_get_const_data (filename);
+
+ hnd = CreateFileA (filename_c, GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (hnd == INVALID_HANDLE_VALUE)
+ {
+ char *emsg = _dbus_win_error_string (GetLastError ());
+ dbus_set_error (error, _dbus_win_error_from_last_error (),
+ "Failed to open \"%s\": %s", filename_c, emsg);
+ _dbus_win_free_error_string (emsg);
+ return FALSE;
+ }
+
+ _dbus_verbose ("file %s hnd %p opened\n", filename_c, hnd);
+
+ fsize = GetFileSize (hnd, &fsize_hi);
+ if (fsize == 0xFFFFFFFF && GetLastError() != NO_ERROR)
+ {
+ char *emsg = _dbus_win_error_string (GetLastError ());
+ dbus_set_error (error, _dbus_win_error_from_last_error (),
+ "Failed to get file size for \"%s\": %s",
+ filename_c, emsg);
+ _dbus_win_free_error_string (emsg);
+
+ _dbus_verbose ("GetFileSize() failed: %s", emsg);
+
+ CloseHandle (hnd);
+
+ return FALSE;
+ }
+
+ if (fsize_hi != 0 || fsize > _DBUS_ONE_MEGABYTE)
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "File size %lu/%lu of \"%s\" is too large.",
+ (unsigned long) fsize_hi,
+ (unsigned long) fsize, filename_c);
+ CloseHandle (hnd);
+ return FALSE;
+ }
+
+ total = 0;
+ orig_len = _dbus_string_get_length (str);
+ if (fsize > 0)
+ {
+ int bytes_read;
+
+ while (total < fsize)
+ {
+ bytes_read = _dbus_file_read (hnd, str, fsize - total, error);
+ if (bytes_read <= 0)
+ {
+ if (bytes_read == 0)
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "Premature EOF reading \"%s\"",
+ filename_c);
+ }
+ else
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+
+ CloseHandle (hnd);
+ _dbus_string_set_length (str, orig_len);
+ return FALSE;
+ }
+ else
+ total += bytes_read;
+ }
+
+ CloseHandle (hnd);
+ return TRUE;
+ }
+ else
+ {
+ CloseHandle (hnd);
+ return TRUE;
+ }
+}
+
+
+/**
+ * Writes a string out to a file. If the file exists,
+ * it will be atomically overwritten by the new data.
+ *
+ * @param str the string to write out
+ * @param filename the file to save string to
+ * @param world_readable if true, ensure file is world readable
+ * @param error error to be filled in on failure
+ * @returns #FALSE on failure
+ */
+dbus_bool_t
+_dbus_string_save_to_file (const DBusString *str,
+ const DBusString *filename,
+ dbus_bool_t world_readable,
+ DBusError *error)
+{
+ HANDLE hnd;
+ int bytes_to_write;
+ const char *filename_c;
+ DBusString tmp_filename;
+ const char *tmp_filename_c;
+ int total;
+ const char *str_c;
+ dbus_bool_t need_unlink;
+ dbus_bool_t retval;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ hnd = INVALID_HANDLE_VALUE;
+ retval = FALSE;
+ need_unlink = FALSE;
+
+ if (!_dbus_string_init (&tmp_filename))
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ return FALSE;
+ }
+
+ if (!_dbus_string_copy (filename, 0, &tmp_filename, 0))
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ _dbus_string_free (&tmp_filename);
+ return FALSE;
+ }
+
+ if (!_dbus_string_append (&tmp_filename, "."))
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ _dbus_string_free (&tmp_filename);
+ return FALSE;
+ }
+
+#define N_TMP_FILENAME_RANDOM_BYTES 8
+ if (!_dbus_generate_random_ascii (&tmp_filename, N_TMP_FILENAME_RANDOM_BYTES,
+ error))
+ {
+ _dbus_string_free (&tmp_filename);
+ return FALSE;
+ }
+
+ filename_c = _dbus_string_get_const_data (filename);
+ tmp_filename_c = _dbus_string_get_const_data (&tmp_filename);
+
+ /* TODO - support world-readable in an atomic fashion */
+ hnd = CreateFileA (tmp_filename_c, GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL,
+ INVALID_HANDLE_VALUE);
+ if (hnd == INVALID_HANDLE_VALUE)
+ {
+ char *emsg = _dbus_win_error_string (GetLastError ());
+ dbus_set_error (error, _dbus_win_error_from_last_error (),
+ "Could not create \"%s\": %s", filename_c, emsg);
+ _dbus_win_free_error_string (emsg);
+ goto out;
+ }
+ if (world_readable)
+ {
+ if (! _dbus_make_file_world_readable (&tmp_filename, error))
+ goto out;
+ }
+
+ _dbus_verbose ("tmp file %s hnd %p opened\n", tmp_filename_c, hnd);
+
+ need_unlink = TRUE;
+
+ total = 0;
+ bytes_to_write = _dbus_string_get_length (str);
+ str_c = _dbus_string_get_const_data (str);
+
+ while (total < bytes_to_write)
+ {
+ DWORD bytes_written;
+ BOOL res;
+
+ res = WriteFile (hnd, str_c + total, bytes_to_write - total,
+ &bytes_written, NULL);
+
+ if (res == 0 || bytes_written <= 0)
+ {
+ char *emsg = _dbus_win_error_string (GetLastError ());
+ dbus_set_error (error, _dbus_win_error_from_last_error (),
+ "Could not write to %s: %s", tmp_filename_c, emsg);
+ _dbus_win_free_error_string (emsg);
+ goto out;
+ }
+
+ total += bytes_written;
+ }
+
+ if (CloseHandle (hnd) == 0)
+ {
+ char *emsg = _dbus_win_error_string (GetLastError ());
+ dbus_set_error (error, _dbus_win_error_from_last_error (),
+ "Could not close file %s: %s", tmp_filename_c, emsg);
+ _dbus_win_free_error_string (emsg);
+ goto out;
+ }
+
+ hnd = INVALID_HANDLE_VALUE;
+
+ /* Unlike rename(), MoveFileEx() can replace existing files */
+ if (!MoveFileExA (tmp_filename_c, filename_c, MOVEFILE_REPLACE_EXISTING))
+ {
+ char *emsg = _dbus_win_error_string (GetLastError ());
+ dbus_set_error (error, _dbus_win_error_from_last_error (),
+ "Could not rename %s to %s: %s",
+ tmp_filename_c, filename_c, emsg);
+ _dbus_win_free_error_string (emsg);
+
+ goto out;
+ }
+
+ need_unlink = FALSE;
+
+ retval = TRUE;
+
+ out:
+ /* close first, then unlink */
+
+ if (hnd != INVALID_HANDLE_VALUE)
+ CloseHandle (hnd);
+
+ if (need_unlink && DeleteFileA (tmp_filename_c) == 0)
+ {
+ char *emsg = _dbus_win_error_string (GetLastError ());
+ _dbus_verbose ("Failed to unlink temp file %s: %s", tmp_filename_c,
+ emsg);
+ _dbus_win_free_error_string (emsg);
+ }
+
+ _dbus_string_free (&tmp_filename);
+
+ if (!retval)
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+
+ return retval;
+}
+
+
+/** Creates the given file, failing if the file already exists.
+ *
+ * @param filename the filename
+ * @param error error location
+ * @returns #TRUE if we created the file and it didn't exist
+ */
+dbus_bool_t
+_dbus_create_file_exclusively (const DBusString *filename,
+ DBusError *error)
+{
+ HANDLE hnd;
+ const char *filename_c;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ filename_c = _dbus_string_get_const_data (filename);
+
+ hnd = CreateFileA (filename_c, GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL,
+ INVALID_HANDLE_VALUE);
+ if (hnd == INVALID_HANDLE_VALUE)
+ {
+ char *emsg = _dbus_win_error_string (GetLastError ());
+ dbus_set_error (error, _dbus_win_error_from_last_error (),
+ "Could not create file %s: %s",
+ filename_c, emsg);
+ _dbus_win_free_error_string (emsg);
+ return FALSE;
+ }
+
+ _dbus_verbose ("exclusive file %s hnd %p opened\n", filename_c, hnd);
+
+ if (CloseHandle (hnd) == 0)
+ {
+ char *emsg = _dbus_win_error_string (GetLastError ());
+ dbus_set_error (error, _dbus_win_error_from_last_error (),
+ "Could not close file %s: %s",
+ filename_c, emsg);
+ _dbus_win_free_error_string (emsg);
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
diff --git a/src/3rdparty/libdbus/dbus/dbus-file.c b/src/3rdparty/libdbus/dbus/dbus-file.c
new file mode 100644
index 00000000..5b122189
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-file.c
@@ -0,0 +1,29 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-sysdeps-unix.c Wrappers around UNIX system/libc features (internal to D-Bus implementation)
+ *
+ * Copyright (C) 2002, 2003, 2006 Red Hat, Inc.
+ * Copyright (C) 2003 CodeFactory AB
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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-file.h"
diff --git a/src/3rdparty/libdbus/dbus/dbus-file.h b/src/3rdparty/libdbus/dbus/dbus-file.h
new file mode 100644
index 00000000..90fc5295
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-file.h
@@ -0,0 +1,67 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-file.h dbus file related stuff (internal to D-Bus implementation)
+ *
+ * Copyright (C) 2002, 2003 Red Hat, Inc.
+ * Copyright (C) 2003 CodeFactory AB
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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
+ *
+ */
+
+#ifndef DBUS_FILE_H
+#define DBUS_FILE_H
+
+//#include <dbus/dbus-types.h>
+#include <dbus/dbus-string.h>
+#include <dbus/dbus-errors.h>
+
+DBUS_BEGIN_DECLS
+
+/**
+ * @addtogroup DBusFile
+ * @{
+ */
+
+/**
+ * File interface
+ */
+dbus_bool_t _dbus_file_exists (const char *file);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_file_get_contents (DBusString *str,
+ const DBusString *filename,
+ DBusError *error);
+dbus_bool_t _dbus_string_save_to_file (const DBusString *str,
+ const DBusString *filename,
+ dbus_bool_t world_readable,
+ DBusError *error);
+
+dbus_bool_t _dbus_make_file_world_readable (const DBusString *filename,
+ DBusError *error);
+
+dbus_bool_t _dbus_create_file_exclusively (const DBusString *filename,
+ DBusError *error);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_delete_file (const DBusString *filename,
+ DBusError *error);
+
+/** @} */
+
+DBUS_END_DECLS
+
+#endif
diff --git a/src/3rdparty/libdbus/dbus/dbus-hash.c b/src/3rdparty/libdbus/dbus/dbus-hash.c
new file mode 100644
index 00000000..2635a154
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-hash.c
@@ -0,0 +1,1595 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-hash.c Generic hash table utility (internal to D-Bus implementation)
+ *
+ * Copyright 1991-1993 The Regents of the University of California.
+ * Copyright 1994 Sun Microsystems, Inc.
+ * Copyright 2002-2005 Red Hat, Inc.
+ * Copyright 2003 Joe Shaw
+ * Copyright 2006 Sjoerd Simons
+ * Copyright 2010 Fridrich Štrba
+ * Copyright 2016 Ralf Habacker
+ * Copyright 2017 Endless Mobile, Inc.
+ * SPDX-License-Identifier: (AFL-2.1 OR GPL-2.0-or-later) AND TCL
+ *
+ * Hash table implementation based on generic/tclHash.c from the Tcl
+ * source code. The original Tcl license applies to portions of the
+ * code from tclHash.c; the Tcl license follows this standad D-Bus
+ * license information.
+ *
+ * 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
+ *
+ */
+/*
+ * The following copyright applies to code from the Tcl distribution.
+ *
+ * Copyright (c) 1991-1993 The Regents of the University of California.
+ * Copyright (c) 1994 Sun Microsystems, Inc.
+ *
+ * This software is copyrighted by the Regents of the University of
+ * California, Sun Microsystems, Inc., Scriptics Corporation, and
+ * other parties. The following terms apply to all files associated
+ * with the software unless explicitly disclaimed in individual files.
+ *
+ * The authors hereby grant permission to use, copy, modify,
+ * distribute, and license this software and its documentation for any
+ * purpose, provided that existing copyright notices are retained in
+ * all copies and that this notice is included verbatim in any
+ * distributions. No written agreement, license, or royalty fee is
+ * required for any of the authorized uses. Modifications to this
+ * software may be copyrighted by their authors and need not follow
+ * the licensing terms described here, provided that the new terms are
+ * clearly indicated on the first page of each file where they apply.
+ *
+ * IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY
+ * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
+ * DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION,
+ * OR ANY DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND
+ * NON-INFRINGEMENT. THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS,
+ * AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE
+ * MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * GOVERNMENT USE: If you are acquiring this software on behalf of the
+ * U.S. government, the Government shall have only "Restricted Rights"
+ * in the software and related documentation as defined in the Federal
+ * Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2). If you
+ * are acquiring the software on behalf of the Department of Defense,
+ * the software shall be classified as "Commercial Computer Software"
+ * and the Government shall have only "Restricted Rights" as defined
+ * in Clause 252.227-7013 (c) (1) of DFARs. Notwithstanding the
+ * foregoing, the authors grant the U.S. Government and others acting
+ * in its behalf permission to use and distribute the software in
+ * accordance with the terms specified in this license.
+ */
+
+#include <config.h>
+#include "dbus-hash.h"
+#include "dbus-internals.h"
+#include "dbus-mempool.h"
+#include <dbus/dbus-test-tap.h>
+
+/**
+ * @defgroup DBusHashTable Hash table
+ * @ingroup DBusInternals
+ * @brief DBusHashTable data structure
+ *
+ * Types and functions related to DBusHashTable.
+ */
+
+/**
+ * @defgroup DBusHashTableInternals Hash table implementation details
+ * @ingroup DBusInternals
+ * @brief DBusHashTable implementation details
+ *
+ * The guts of DBusHashTable.
+ *
+ * @{
+ */
+
+/**
+ * When there are this many entries per bucket, on average, rebuild
+ * the hash table to make it larger.
+ */
+#define REBUILD_MULTIPLIER 3
+
+/**
+ * Takes a preliminary integer hash value and produces an index into a
+ * hash tables bucket list. The idea is to make it so that
+ * preliminary values that are arbitrarily similar will end up in
+ * different buckets. The hash function was taken from a
+ * random-number generator. (This is used to hash integers.)
+ *
+ * The down_shift drops off the high bits of the hash index, and
+ * decreases as we increase the number of hash buckets (to keep more
+ * range in the hash index). The mask also strips high bits and strips
+ * fewer high bits as the number of hash buckets increases.
+ * I don't understand two things: why is the initial downshift 28
+ * to keep 4 bits when the initial mask is 011 to keep 2 bits,
+ * and why do we have both a mask and a downshift?
+ *
+ */
+#define RANDOM_INDEX(table, i) \
+ (((((intptr_t) (i))*1103515245) >> (table)->down_shift) & (table)->mask)
+
+/**
+ * Initial number of buckets in hash table (hash table statically
+ * allocates its buckets for this size and below).
+ * The initial mask has to be synced to this.
+ */
+#define DBUS_SMALL_HASH_TABLE 4
+
+/**
+ * Typedef for DBusHashEntry
+ */
+typedef struct DBusHashEntry DBusHashEntry;
+
+/**
+ * @brief Internal representation of a hash entry.
+ *
+ * A single entry (key-value pair) in the hash table.
+ * Internal to hash table implementation.
+ */
+struct DBusHashEntry
+{
+ DBusHashEntry *next; /**< Pointer to next entry in this
+ * hash bucket, or #NULL for end of
+ * chain.
+ */
+ void *key; /**< Hash key */
+ void *value; /**< Hash value */
+};
+
+/**
+ * Function used to find and optionally create a hash entry.
+ */
+typedef DBusHashEntry* (* DBusFindEntryFunction) (DBusHashTable *table,
+ void *key,
+ dbus_bool_t create_if_not_found,
+ DBusHashEntry ***bucket,
+ DBusPreallocatedHash *preallocated);
+
+/**
+ * @brief Internals of DBusHashTable.
+ *
+ * Hash table internals. Hash tables are opaque objects, they must be
+ * used via accessor functions.
+ */
+struct DBusHashTable {
+ int refcount; /**< Reference count */
+
+ DBusHashEntry **buckets; /**< Pointer to bucket array. Each
+ * element points to first entry in
+ * bucket's hash chain, or #NULL.
+ */
+ DBusHashEntry *static_buckets[DBUS_SMALL_HASH_TABLE];
+ /**< Bucket array used for small tables
+ * (to avoid mallocs and frees).
+ */
+ int n_buckets; /**< Total number of buckets allocated
+ * at **buckets.
+ */
+ int n_entries; /**< Total number of entries present
+ * in table.
+ */
+ int hi_rebuild_size; /**< Enlarge table when n_entries gets
+ * to be this large.
+ */
+ int lo_rebuild_size; /**< Shrink table when n_entries gets
+ * below this.
+ */
+ int down_shift; /**< Shift count used in hashing
+ * function. Designed to use high-
+ * order bits of randomized keys.
+ */
+ int mask; /**< Mask value used in hashing
+ * function.
+ */
+ DBusHashType key_type; /**< Type of keys used in this table */
+
+
+ DBusFindEntryFunction find_function; /**< Function for finding entries */
+
+ DBusFreeFunction free_key_function; /**< Function to free keys */
+ DBusFreeFunction free_value_function; /**< Function to free values */
+
+ DBusMemPool *entry_pool; /**< Memory pool for hash entries */
+};
+
+/**
+ * @brief Internals of DBusHashIter.
+ */
+typedef struct
+{
+ DBusHashTable *table; /**< Pointer to table containing entry. */
+ DBusHashEntry **bucket; /**< Pointer to bucket that points to
+ * first entry in this entry's chain:
+ * used for deleting the entry.
+ */
+ DBusHashEntry *entry; /**< Current hash entry */
+ DBusHashEntry *next_entry; /**< Next entry to be iterated onto in current bucket */
+ int next_bucket; /**< index of next bucket */
+ int n_entries_on_init; /**< used to detect table resize since initialization */
+} DBusRealHashIter;
+
+_DBUS_STATIC_ASSERT (sizeof (DBusRealHashIter) == sizeof (DBusHashIter));
+
+static DBusHashEntry* find_direct_function (DBusHashTable *table,
+ void *key,
+ dbus_bool_t create_if_not_found,
+ DBusHashEntry ***bucket,
+ DBusPreallocatedHash *preallocated);
+static DBusHashEntry* find_string_function (DBusHashTable *table,
+ void *key,
+ dbus_bool_t create_if_not_found,
+ DBusHashEntry ***bucket,
+ DBusPreallocatedHash *preallocated);
+static unsigned int string_hash (const char *str);
+static dbus_bool_t rebuild_table (DBusHashTable *table);
+static DBusHashEntry* alloc_entry (DBusHashTable *table);
+static void remove_entry (DBusHashTable *table,
+ DBusHashEntry **bucket,
+ DBusHashEntry *entry);
+static void free_entry (DBusHashTable *table,
+ DBusHashEntry *entry);
+static void free_entry_data (DBusHashTable *table,
+ DBusHashEntry *entry);
+
+
+/** @} */
+
+/**
+ * @addtogroup DBusHashTable
+ * @{
+ */
+
+/**
+ * @typedef DBusHashIter
+ *
+ * Public opaque hash table iterator object.
+ */
+
+/**
+ * @typedef DBusHashTable
+ *
+ * Public opaque hash table object.
+ */
+
+/**
+ * @typedef DBusHashType
+ *
+ * Indicates the type of a key in the hash table.
+ */
+
+/**
+ * Constructs a new hash table. Should be freed with
+ * _dbus_hash_table_unref(). If memory cannot be
+ * allocated for the hash table, returns #NULL.
+ *
+ * @param type the type of hash key to use.
+ * @param key_free_function function to free hash keys.
+ * @param value_free_function function to free hash values.
+ * @returns a new DBusHashTable or #NULL if no memory.
+ */
+DBusHashTable*
+_dbus_hash_table_new (DBusHashType type,
+ DBusFreeFunction key_free_function,
+ DBusFreeFunction value_free_function)
+{
+ DBusHashTable *table;
+ DBusMemPool *entry_pool;
+
+ table = dbus_new0 (DBusHashTable, 1);
+ if (table == NULL)
+ return NULL;
+
+ entry_pool = _dbus_mem_pool_new (sizeof (DBusHashEntry), TRUE);
+ if (entry_pool == NULL)
+ {
+ dbus_free (table);
+ return NULL;
+ }
+
+ table->refcount = 1;
+ table->entry_pool = entry_pool;
+
+ _dbus_assert (DBUS_SMALL_HASH_TABLE == _DBUS_N_ELEMENTS (table->static_buckets));
+
+ table->buckets = table->static_buckets;
+ table->n_buckets = DBUS_SMALL_HASH_TABLE;
+ table->n_entries = 0;
+ table->hi_rebuild_size = DBUS_SMALL_HASH_TABLE * REBUILD_MULTIPLIER;
+ table->lo_rebuild_size = 0;
+ table->down_shift = 28;
+ table->mask = 3;
+ table->key_type = type;
+
+ _dbus_assert (table->mask < table->n_buckets);
+
+ switch (table->key_type)
+ {
+ case DBUS_HASH_INT:
+ case DBUS_HASH_UINTPTR:
+ table->find_function = find_direct_function;
+ break;
+ case DBUS_HASH_STRING:
+ table->find_function = find_string_function;
+ break;
+ default:
+ _dbus_assert_not_reached ("Unknown hash table type");
+ break;
+ }
+
+ table->free_key_function = key_free_function;
+ table->free_value_function = value_free_function;
+
+ return table;
+}
+
+
+/**
+ * Increments the reference count for a hash table.
+ *
+ * @param table the hash table to add a reference to.
+ * @returns the hash table.
+ */
+DBusHashTable *
+_dbus_hash_table_ref (DBusHashTable *table)
+{
+ table->refcount += 1;
+
+ return table;
+}
+
+/**
+ * Decrements the reference count for a hash table,
+ * freeing the hash table if the count reaches zero.
+ *
+ * @param table the hash table to remove a reference from.
+ */
+void
+_dbus_hash_table_unref (DBusHashTable *table)
+{
+ table->refcount -= 1;
+
+ if (table->refcount == 0)
+ {
+#if 0
+ DBusHashEntry *entry;
+ DBusHashEntry *next;
+ int i;
+
+ /* Free the entries in the table. */
+ for (i = 0; i < table->n_buckets; i++)
+ {
+ entry = table->buckets[i];
+ while (entry != NULL)
+ {
+ next = entry->next;
+
+ free_entry (table, entry);
+
+ entry = next;
+ }
+ }
+#else
+ DBusHashEntry *entry;
+ int i;
+
+ /* Free the entries in the table. */
+ for (i = 0; i < table->n_buckets; i++)
+ {
+ entry = table->buckets[i];
+ while (entry != NULL)
+ {
+ free_entry_data (table, entry);
+
+ entry = entry->next;
+ }
+ }
+ /* We can do this very quickly with memory pools ;-) */
+ _dbus_mem_pool_free (table->entry_pool);
+#endif
+
+ /* Free the bucket array, if it was dynamically allocated. */
+ if (table->buckets != table->static_buckets)
+ dbus_free (table->buckets);
+
+ dbus_free (table);
+ }
+}
+
+/**
+ * Removed all entries from a hash table.
+ *
+ * @param table the hash table to remove all entries from.
+ */
+void
+_dbus_hash_table_remove_all (DBusHashTable *table)
+{
+ DBusHashIter iter;
+ _dbus_hash_iter_init (table, &iter);
+ while (_dbus_hash_iter_next (&iter))
+ {
+ _dbus_hash_iter_remove_entry(&iter);
+ }
+}
+
+static DBusHashEntry*
+alloc_entry (DBusHashTable *table)
+{
+ DBusHashEntry *entry;
+
+ entry = _dbus_mem_pool_alloc (table->entry_pool);
+
+ return entry;
+}
+
+static void
+free_entry_data (DBusHashTable *table,
+ DBusHashEntry *entry)
+{
+ if (table->free_key_function)
+ (* table->free_key_function) (entry->key);
+ if (table->free_value_function)
+ (* table->free_value_function) (entry->value);
+}
+
+static void
+free_entry (DBusHashTable *table,
+ DBusHashEntry *entry)
+{
+ free_entry_data (table, entry);
+ _dbus_mem_pool_dealloc (table->entry_pool, entry);
+}
+
+static void
+remove_entry (DBusHashTable *table,
+ DBusHashEntry **bucket,
+ DBusHashEntry *entry)
+{
+ _dbus_assert (table != NULL);
+ _dbus_assert (bucket != NULL);
+ _dbus_assert (*bucket != NULL);
+ _dbus_assert (entry != NULL);
+
+ if (*bucket == entry)
+ *bucket = entry->next;
+ else
+ {
+ DBusHashEntry *prev;
+ prev = *bucket;
+
+ while (prev->next != entry)
+ prev = prev->next;
+
+ _dbus_assert (prev != NULL);
+
+ prev->next = entry->next;
+ }
+
+ table->n_entries -= 1;
+ free_entry (table, entry);
+}
+
+/**
+ * Initializes a hash table iterator. To iterate over all entries in a
+ * hash table, use the following code (the printf assumes a hash
+ * from strings to strings obviously):
+ *
+ * @code
+ * DBusHashIter iter;
+ *
+ * _dbus_hash_iter_init (table, &iter);
+ * while (_dbus_hash_iter_next (&iter))
+ * {
+ * printf ("The first key is %s and value is %s\n",
+ * _dbus_hash_iter_get_string_key (&iter),
+ * _dbus_hash_iter_get_value (&iter));
+ * }
+ *
+ *
+ * @endcode
+ *
+ * The iterator is initialized pointing "one before" the first hash
+ * entry. The first call to _dbus_hash_iter_next() moves it onto
+ * the first valid entry or returns #FALSE if the hash table is
+ * empty. Subsequent calls move to the next valid entry or return
+ * #FALSE if there are no more entries.
+ *
+ * Note that it is guaranteed to be safe to remove a hash entry during
+ * iteration, but it is not safe to add a hash entry.
+ *
+ * @param table the hash table to iterate over.
+ * @param iter the iterator to initialize.
+ */
+void
+_dbus_hash_iter_init (DBusHashTable *table,
+ DBusHashIter *iter)
+{
+ DBusRealHashIter *real;
+
+ _DBUS_STATIC_ASSERT (sizeof (DBusHashIter) == sizeof (DBusRealHashIter));
+
+ real = (DBusRealHashIter*) iter;
+
+ real->table = table;
+ real->bucket = NULL;
+ real->entry = NULL;
+ real->next_entry = NULL;
+ real->next_bucket = 0;
+ real->n_entries_on_init = table->n_entries;
+}
+
+/**
+ * Move the hash iterator forward one step, to the next hash entry.
+ * The documentation for _dbus_hash_iter_init() explains in more
+ * detail.
+ *
+ * @param iter the iterator to move forward.
+ * @returns #FALSE if there are no more entries to move to.
+ */
+dbus_bool_t
+_dbus_hash_iter_next (DBusHashIter *iter)
+{
+ DBusRealHashIter *real;
+
+ _DBUS_STATIC_ASSERT (sizeof (DBusHashIter) == sizeof (DBusRealHashIter));
+
+ real = (DBusRealHashIter*) iter;
+
+ /* if this assertion failed someone probably added hash entries
+ * during iteration, which is bad.
+ */
+ _dbus_assert (real->n_entries_on_init >= real->table->n_entries);
+
+ /* Remember that real->entry may have been deleted */
+
+ while (real->next_entry == NULL)
+ {
+ if (real->next_bucket >= real->table->n_buckets)
+ {
+ /* invalidate iter and return false */
+ real->entry = NULL;
+ real->table = NULL;
+ real->bucket = NULL;
+ return FALSE;
+ }
+
+ real->bucket = &(real->table->buckets[real->next_bucket]);
+ real->next_entry = *(real->bucket);
+ real->next_bucket += 1;
+ }
+
+ _dbus_assert (real->next_entry != NULL);
+ _dbus_assert (real->bucket != NULL);
+
+ real->entry = real->next_entry;
+ real->next_entry = real->entry->next;
+
+ return TRUE;
+}
+
+/**
+ * Removes the current entry from the hash table.
+ * If a key_free_function or value_free_function
+ * was provided to _dbus_hash_table_new(),
+ * frees the key and/or value for this entry.
+ *
+ * @param iter the hash table iterator.
+ */
+void
+_dbus_hash_iter_remove_entry (DBusHashIter *iter)
+{
+ DBusRealHashIter *real;
+
+ real = (DBusRealHashIter*) iter;
+
+ _dbus_assert (real->table != NULL);
+ _dbus_assert (real->entry != NULL);
+ _dbus_assert (real->bucket != NULL);
+
+ remove_entry (real->table, real->bucket, real->entry);
+
+ real->entry = NULL; /* make it crash if you try to use this entry */
+}
+
+/**
+ * Gets the value of the current entry.
+ *
+ * @param iter the hash table iterator.
+ */
+void*
+_dbus_hash_iter_get_value (DBusHashIter *iter)
+{
+ DBusRealHashIter *real;
+
+ real = (DBusRealHashIter*) iter;
+
+ _dbus_assert (real->table != NULL);
+ _dbus_assert (real->entry != NULL);
+
+ return real->entry->value;
+}
+
+/**
+ * Sets the value of the current entry.
+ * If the hash table has a value_free_function
+ * it will be used to free the previous value.
+ * The hash table will own the passed-in value
+ * (it will not be copied).
+ *
+ * @param iter the hash table iterator.
+ * @param value the new value.
+ */
+void
+_dbus_hash_iter_set_value (DBusHashIter *iter,
+ void *value)
+{
+ DBusRealHashIter *real;
+
+ real = (DBusRealHashIter*) iter;
+
+ _dbus_assert (real->table != NULL);
+ _dbus_assert (real->entry != NULL);
+
+ if (real->table->free_value_function && value != real->entry->value)
+ (* real->table->free_value_function) (real->entry->value);
+
+ real->entry->value = value;
+}
+
+/**
+ * Gets the key for the current entry.
+ * Only works for hash tables of type #DBUS_HASH_INT.
+ *
+ * @param iter the hash table iterator.
+ */
+int
+_dbus_hash_iter_get_int_key (DBusHashIter *iter)
+{
+ DBusRealHashIter *real;
+
+ real = (DBusRealHashIter*) iter;
+
+ _dbus_assert (real->table != NULL);
+ _dbus_assert (real->entry != NULL);
+
+ return _DBUS_POINTER_TO_INT (real->entry->key);
+}
+
+/**
+ * Gets the key for the current entry.
+ * Only works for hash tables of type #DBUS_HASH_UINTPTR.
+ *
+ * @param iter the hash table iterator.
+ */
+uintptr_t
+_dbus_hash_iter_get_uintptr_key (DBusHashIter *iter)
+{
+ DBusRealHashIter *real;
+
+ real = (DBusRealHashIter*) iter;
+
+ _dbus_assert (real->table != NULL);
+ _dbus_assert (real->entry != NULL);
+
+ return (uintptr_t) real->entry->key;
+}
+
+/**
+ * Gets the key for the current entry.
+ * Only works for hash tables of type #DBUS_HASH_STRING
+ * @param iter the hash table iterator.
+ */
+const char*
+_dbus_hash_iter_get_string_key (DBusHashIter *iter)
+{
+ DBusRealHashIter *real;
+
+ real = (DBusRealHashIter*) iter;
+
+ _dbus_assert (real->table != NULL);
+ _dbus_assert (real->entry != NULL);
+
+ return real->entry->key;
+}
+
+/**
+ * A low-level but efficient interface for manipulating the hash
+ * table. It's efficient because you can get, set, and optionally
+ * create the hash entry while only running the hash function one
+ * time.
+ *
+ * Note that while calling _dbus_hash_iter_next() on the iterator
+ * filled in by this function may work, it's completely
+ * undefined which entries are after this iter and which
+ * are before it. So it would be silly to iterate using this
+ * iterator.
+ *
+ * If the hash entry is created, its value will be initialized
+ * to all bits zero.
+ *
+ * #FALSE may be returned due to memory allocation failure, or
+ * because create_if_not_found was #FALSE and the entry
+ * did not exist.
+ *
+ * If create_if_not_found is #TRUE, the hash
+ * table takes ownership of the key that's passed in (either using it to create
+ * the entry, or freeing it immediately).
+ *
+ * For a hash table of type #DBUS_HASH_INT, cast the int
+ * key to the key parameter using #_DBUS_INT_TO_POINTER().
+ *
+ * @param table the hash table.
+ * @param key the hash key.
+ * @param create_if_not_found if #TRUE, create the entry if it didn't exist.
+ * @param iter the iterator to initialize.
+ * @returns #TRUE if the hash entry now exists (and the iterator is thus valid).
+ */
+dbus_bool_t
+_dbus_hash_iter_lookup (DBusHashTable *table,
+ void *key,
+ dbus_bool_t create_if_not_found,
+ DBusHashIter *iter)
+{
+ DBusRealHashIter *real;
+ DBusHashEntry *entry = NULL;
+ DBusHashEntry **bucket = NULL;
+
+ _DBUS_STATIC_ASSERT (sizeof (DBusHashIter) == sizeof (DBusRealHashIter));
+
+ real = (DBusRealHashIter*) iter;
+
+ entry = (* table->find_function) (table, key, create_if_not_found, &bucket, NULL);
+
+ /* entry == NULL means not found, and either !create_if_not_found or OOM */
+ if (entry == NULL)
+ return FALSE;
+
+ _dbus_assert (bucket != NULL);
+ _dbus_assert (table->n_buckets >= 1);
+ _dbus_assert (bucket >= table->buckets);
+ _dbus_assert (bucket <= &table->buckets[table->n_buckets - 1]);
+
+ if (create_if_not_found)
+ {
+ if (table->free_key_function && entry->key != key)
+ (* table->free_key_function) (entry->key);
+
+ entry->key = key;
+ }
+
+ real->table = table;
+ real->bucket = bucket;
+ real->entry = entry;
+ real->next_entry = entry->next;
+ real->next_bucket = (bucket - table->buckets) + 1;
+ real->n_entries_on_init = table->n_entries;
+
+ _dbus_assert (real->next_bucket >= 0);
+ _dbus_assert (real->next_bucket <= table->n_buckets);
+ _dbus_assert (&(table->buckets[real->next_bucket-1]) == real->bucket);
+
+ return TRUE;
+}
+
+static void
+add_allocated_entry (DBusHashTable *table,
+ DBusHashEntry *entry,
+ unsigned int idx,
+ void *key,
+ DBusHashEntry ***bucket)
+{
+ DBusHashEntry **b;
+
+ entry->key = key;
+
+ b = &(table->buckets[idx]);
+ entry->next = *b;
+ *b = entry;
+
+ if (bucket)
+ *bucket = b;
+
+ table->n_entries += 1;
+
+ /* note we ONLY rebuild when ADDING - because you can iterate over a
+ * table and remove entries safely.
+ */
+ if (table->n_entries >= table->hi_rebuild_size ||
+ table->n_entries < table->lo_rebuild_size)
+ {
+ if (!rebuild_table (table))
+ return;
+
+ if (bucket)
+ {
+ /* Recalculate hash for the new table size */
+ switch (table->key_type)
+ {
+ case DBUS_HASH_STRING:
+ idx = string_hash (entry->key) & table->mask;
+ break;
+
+ case DBUS_HASH_INT:
+ case DBUS_HASH_UINTPTR:
+ idx = RANDOM_INDEX (table, entry->key);
+ break;
+
+ default:
+ idx = 0;
+ _dbus_assert_not_reached ("Unknown hash table type");
+ break;
+ }
+
+ *bucket = &(table->buckets[idx]);
+ }
+ }
+}
+
+static DBusHashEntry*
+add_entry (DBusHashTable *table,
+ unsigned int idx,
+ void *key,
+ DBusHashEntry ***bucket,
+ DBusPreallocatedHash *preallocated)
+{
+ DBusHashEntry *entry;
+
+ if (preallocated == NULL)
+ {
+ entry = alloc_entry (table);
+ if (entry == NULL)
+ {
+ if (bucket)
+ *bucket = NULL;
+ return NULL;
+ }
+ }
+ else
+ {
+ entry = (DBusHashEntry*) preallocated;
+ }
+
+ add_allocated_entry (table, entry, idx, key, bucket);
+ _dbus_assert (bucket == NULL || *bucket != NULL);
+
+ return entry;
+}
+
+/* This is g_str_hash from GLib which was
+ * extensively discussed/tested/profiled
+ */
+static unsigned int
+string_hash (const char *str)
+{
+ const char *p = str;
+ unsigned int h = *p;
+
+ if (h)
+ for (p += 1; *p != '\0'; p++)
+ h = (h << 5) - h + *p;
+
+ return h;
+}
+
+/** Key comparison function */
+typedef int (* KeyCompareFunc) (const void *key_a, const void *key_b);
+
+static DBusHashEntry*
+find_generic_function (DBusHashTable *table,
+ void *key,
+ unsigned int idx,
+ KeyCompareFunc compare_func,
+ dbus_bool_t create_if_not_found,
+ DBusHashEntry ***bucket,
+ DBusPreallocatedHash *preallocated)
+{
+ DBusHashEntry *entry;
+
+ if (bucket)
+ *bucket = NULL;
+
+ /* Search all of the entries in this bucket. */
+ entry = table->buckets[idx];
+ while (entry != NULL)
+ {
+ if ((compare_func == NULL && key == entry->key) ||
+ (compare_func != NULL && (* compare_func) (key, entry->key) == 0))
+ {
+ if (bucket)
+ *bucket = &(table->buckets[idx]);
+
+ if (preallocated)
+ _dbus_hash_table_free_preallocated_entry (table, preallocated);
+
+ return entry;
+ }
+
+ entry = entry->next;
+ }
+
+ if (create_if_not_found)
+ {
+ entry = add_entry (table, idx, key, bucket, preallocated);
+
+ if (entry == NULL) /* OOM */
+ return NULL;
+
+ _dbus_assert (bucket == NULL || *bucket != NULL);
+ }
+ else if (preallocated)
+ {
+ _dbus_hash_table_free_preallocated_entry (table, preallocated);
+ }
+
+ return entry;
+}
+
+static DBusHashEntry*
+find_string_function (DBusHashTable *table,
+ void *key,
+ dbus_bool_t create_if_not_found,
+ DBusHashEntry ***bucket,
+ DBusPreallocatedHash *preallocated)
+{
+ unsigned int idx;
+
+ idx = string_hash (key) & table->mask;
+
+ return find_generic_function (table, key, idx,
+ (KeyCompareFunc) strcmp, create_if_not_found, bucket,
+ preallocated);
+}
+
+static DBusHashEntry*
+find_direct_function (DBusHashTable *table,
+ void *key,
+ dbus_bool_t create_if_not_found,
+ DBusHashEntry ***bucket,
+ DBusPreallocatedHash *preallocated)
+{
+ unsigned int idx;
+
+ idx = RANDOM_INDEX (table, key) & table->mask;
+
+
+ return find_generic_function (table, key, idx,
+ NULL, create_if_not_found, bucket,
+ preallocated);
+}
+
+/* Return FALSE if nothing happened. */
+static dbus_bool_t
+rebuild_table (DBusHashTable *table)
+{
+ int old_size;
+ int new_buckets;
+ DBusHashEntry **old_buckets;
+ DBusHashEntry **old_chain;
+ DBusHashEntry *entry;
+ dbus_bool_t growing;
+
+ /*
+ * Allocate and initialize the new bucket array, and set up
+ * hashing constants for new array size.
+ */
+
+ growing = table->n_entries >= table->hi_rebuild_size;
+
+ old_size = table->n_buckets;
+ old_buckets = table->buckets;
+
+ if (growing)
+ {
+ /* overflow paranoia */
+ if (table->n_buckets < _DBUS_INT_MAX / 4 &&
+ table->down_shift >= 2)
+ new_buckets = table->n_buckets * 4;
+ else
+ return FALSE; /* can't grow any more */
+ }
+ else
+ {
+ new_buckets = table->n_buckets / 4;
+ if (new_buckets < DBUS_SMALL_HASH_TABLE)
+ return FALSE; /* don't bother shrinking this far */
+ }
+
+ table->buckets = dbus_new0 (DBusHashEntry*, new_buckets);
+ if (table->buckets == NULL)
+ {
+ /* out of memory, yay - just don't reallocate, the table will
+ * still work, albeit more slowly.
+ */
+ table->buckets = old_buckets;
+ return FALSE;
+ }
+
+ table->n_buckets = new_buckets;
+
+ if (growing)
+ {
+ table->lo_rebuild_size = table->hi_rebuild_size;
+ table->hi_rebuild_size *= 4;
+
+ table->down_shift -= 2; /* keep 2 more high bits */
+ table->mask = (table->mask << 2) + 3; /* keep 2 more high bits */
+ }
+ else
+ {
+ table->hi_rebuild_size = table->lo_rebuild_size;
+ table->lo_rebuild_size /= 4;
+
+ table->down_shift += 2; /* keep 2 fewer high bits */
+ table->mask = table->mask >> 2; /* keep 2 fewer high bits */
+ }
+
+#if 0
+ printf ("%s table to lo = %d hi = %d downshift = %d mask = 0x%x\n",
+ growing ? "GROW" : "SHRINK",
+ table->lo_rebuild_size,
+ table->hi_rebuild_size,
+ table->down_shift,
+ table->mask);
+#endif
+
+ _dbus_assert (table->lo_rebuild_size >= 0);
+ _dbus_assert (table->hi_rebuild_size > table->lo_rebuild_size);
+ _dbus_assert (table->down_shift >= 0);
+ _dbus_assert (table->mask != 0);
+ /* the mask is essentially the max index */
+ _dbus_assert (table->mask < table->n_buckets);
+
+ /*
+ * Rehash all of the existing entries into the new bucket array.
+ */
+
+ for (old_chain = old_buckets; old_size > 0; old_size--, old_chain++)
+ {
+ for (entry = *old_chain; entry != NULL; entry = *old_chain)
+ {
+ unsigned int idx;
+ DBusHashEntry **bucket;
+
+ *old_chain = entry->next;
+ switch (table->key_type)
+ {
+ case DBUS_HASH_STRING:
+ idx = string_hash (entry->key) & table->mask;
+ break;
+ case DBUS_HASH_INT:
+ case DBUS_HASH_UINTPTR:
+ idx = RANDOM_INDEX (table, entry->key);
+ break;
+ default:
+ idx = 0;
+ _dbus_assert_not_reached ("Unknown hash table type");
+ break;
+ }
+
+ bucket = &(table->buckets[idx]);
+ entry->next = *bucket;
+ *bucket = entry;
+ }
+ }
+
+ /* Free the old bucket array, if it was dynamically allocated. */
+
+ if (old_buckets != table->static_buckets)
+ dbus_free (old_buckets);
+
+ return TRUE;
+}
+
+/**
+ * Looks up the value for a given string in a hash table
+ * of type #DBUS_HASH_STRING. Returns %NULL if the value
+ * is not present. (A not-present entry is indistinguishable
+ * from an entry with a value of %NULL.)
+ * @param table the hash table.
+ * @param key the string to look up.
+ * @returns the value of the hash entry.
+ */
+void*
+_dbus_hash_table_lookup_string (DBusHashTable *table,
+ const char *key)
+{
+ DBusHashEntry *entry;
+
+ _dbus_assert (table->key_type == DBUS_HASH_STRING);
+
+ entry = (* table->find_function) (table, (char*) key, FALSE, NULL, NULL);
+
+ if (entry)
+ return entry->value;
+ else
+ return NULL;
+}
+
+/**
+ * Looks up the value for a given integer in a hash table
+ * of type #DBUS_HASH_INT. Returns %NULL if the value
+ * is not present. (A not-present entry is indistinguishable
+ * from an entry with a value of %NULL.)
+ * @param table the hash table.
+ * @param key the integer to look up.
+ * @returns the value of the hash entry.
+ */
+void*
+_dbus_hash_table_lookup_int (DBusHashTable *table,
+ int key)
+{
+ DBusHashEntry *entry;
+
+ _dbus_assert (table->key_type == DBUS_HASH_INT);
+
+ entry = (* table->find_function) (table, _DBUS_INT_TO_POINTER (key), FALSE, NULL, NULL);
+
+ if (entry)
+ return entry->value;
+ else
+ return NULL;
+}
+
+/**
+ * Looks up the value for a given integer in a hash table
+ * of type #DBUS_HASH_UINTPTR. Returns %NULL if the value
+ * is not present. (A not-present entry is indistinguishable
+ * from an entry with a value of %NULL.)
+ * @param table the hash table.
+ * @param key the integer to look up.
+ * @returns the value of the hash entry.
+ */
+void*
+_dbus_hash_table_lookup_uintptr (DBusHashTable *table,
+ uintptr_t key)
+{
+ DBusHashEntry *entry;
+
+ _dbus_assert (table->key_type == DBUS_HASH_UINTPTR);
+
+ entry = (* table->find_function) (table, (void*) key, FALSE, NULL, NULL);
+
+ if (entry)
+ return entry->value;
+ else
+ return NULL;
+}
+
+/**
+ * Removes the hash entry for the given key. If no hash entry
+ * for the key exists, does nothing.
+ *
+ * @param table the hash table.
+ * @param key the hash key.
+ * @returns #TRUE if the entry existed
+ */
+dbus_bool_t
+_dbus_hash_table_remove_string (DBusHashTable *table,
+ const char *key)
+{
+ DBusHashEntry *entry;
+ DBusHashEntry **bucket;
+
+ _dbus_assert (table->key_type == DBUS_HASH_STRING);
+
+ entry = (* table->find_function) (table, (char*) key, FALSE, &bucket, NULL);
+
+ if (entry)
+ {
+ remove_entry (table, bucket, entry);
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+/**
+ * Removes the hash entry for the given key. If no hash entry
+ * for the key exists, does nothing.
+ *
+ * @param table the hash table.
+ * @param key the hash key.
+ * @returns #TRUE if the entry existed
+ */
+dbus_bool_t
+_dbus_hash_table_remove_int (DBusHashTable *table,
+ int key)
+{
+ DBusHashEntry *entry;
+ DBusHashEntry **bucket;
+
+ _dbus_assert (table->key_type == DBUS_HASH_INT);
+
+ entry = (* table->find_function) (table, _DBUS_INT_TO_POINTER (key), FALSE, &bucket, NULL);
+
+ if (entry)
+ {
+ remove_entry (table, bucket, entry);
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+/**
+ * Removes the hash entry for the given key. If no hash entry
+ * for the key exists, does nothing.
+ *
+ * @param table the hash table.
+ * @param key the hash key.
+ * @returns #TRUE if the entry existed
+ */
+dbus_bool_t
+_dbus_hash_table_remove_uintptr (DBusHashTable *table,
+ uintptr_t key)
+{
+ DBusHashEntry *entry;
+ DBusHashEntry **bucket;
+
+ _dbus_assert (table->key_type == DBUS_HASH_UINTPTR);
+
+ entry = (* table->find_function) (table, (void*) key, FALSE, &bucket, NULL);
+
+ if (entry)
+ {
+ remove_entry (table, bucket, entry);
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+/**
+ * Creates a hash entry with the given key and value.
+ * The key and value are not copied; they are stored
+ * in the hash table by reference. If an entry with the
+ * given key already exists, the previous key and value
+ * are overwritten (and freed if the hash table has
+ * a key_free_function and/or value_free_function).
+ *
+ * Returns #FALSE if memory for the new hash entry
+ * can't be allocated.
+ *
+ * @param table the hash table.
+ * @param key the hash entry key.
+ * @param value the hash entry value.
+ */
+dbus_bool_t
+_dbus_hash_table_insert_string (DBusHashTable *table,
+ char *key,
+ void *value)
+{
+ DBusPreallocatedHash *preallocated;
+
+ _dbus_assert (table->key_type == DBUS_HASH_STRING);
+
+ preallocated = _dbus_hash_table_preallocate_entry (table);
+ if (preallocated == NULL)
+ return FALSE;
+
+ _dbus_hash_table_insert_string_preallocated (table, preallocated,
+ key, value);
+
+ return TRUE;
+}
+
+/**
+ * Creates a hash entry with the given key and value.
+ * The key and value are not copied; they are stored
+ * in the hash table by reference. If an entry with the
+ * given key already exists, the previous key and value
+ * are overwritten (and freed if the hash table has
+ * a key_free_function and/or value_free_function).
+ *
+ * Returns #FALSE if memory for the new hash entry
+ * can't be allocated.
+ *
+ * @param table the hash table.
+ * @param key the hash entry key.
+ * @param value the hash entry value.
+ */
+dbus_bool_t
+_dbus_hash_table_insert_int (DBusHashTable *table,
+ int key,
+ void *value)
+{
+ DBusHashEntry *entry;
+
+ _dbus_assert (table->key_type == DBUS_HASH_INT);
+
+ entry = (* table->find_function) (table, _DBUS_INT_TO_POINTER (key), TRUE, NULL, NULL);
+
+ if (entry == NULL)
+ return FALSE; /* no memory */
+
+ if (table->free_key_function && entry->key != _DBUS_INT_TO_POINTER (key))
+ (* table->free_key_function) (entry->key);
+
+ if (table->free_value_function && entry->value != value)
+ (* table->free_value_function) (entry->value);
+
+ entry->key = _DBUS_INT_TO_POINTER (key);
+ entry->value = value;
+
+ return TRUE;
+}
+
+/**
+ * Creates a hash entry with the given key and value.
+ * The key and value are not copied; they are stored
+ * in the hash table by reference. If an entry with the
+ * given key already exists, the previous key and value
+ * are overwritten (and freed if the hash table has
+ * a key_free_function and/or value_free_function).
+ *
+ * Returns #FALSE if memory for the new hash entry
+ * can't be allocated.
+ *
+ * @param table the hash table.
+ * @param key the hash entry key.
+ * @param value the hash entry value.
+ */
+dbus_bool_t
+_dbus_hash_table_insert_uintptr (DBusHashTable *table,
+ uintptr_t key,
+ void *value)
+{
+ DBusHashEntry *entry;
+
+ _dbus_assert (table->key_type == DBUS_HASH_UINTPTR);
+
+ entry = (* table->find_function) (table, (void*) key, TRUE, NULL, NULL);
+
+ if (entry == NULL)
+ return FALSE; /* no memory */
+
+ if (table->free_key_function && entry->key != (void*) key)
+ (* table->free_key_function) (entry->key);
+
+ if (table->free_value_function && entry->value != value)
+ (* table->free_value_function) (entry->value);
+
+ entry->key = (void*) key;
+ entry->value = value;
+
+ return TRUE;
+}
+
+/**
+ * Preallocate an opaque data blob that allows us to insert into the
+ * hash table at a later time without allocating any memory.
+ *
+ * @param table the hash table
+ * @returns the preallocated data, or #NULL if no memory
+ */
+DBusPreallocatedHash*
+_dbus_hash_table_preallocate_entry (DBusHashTable *table)
+{
+ DBusHashEntry *entry;
+
+ entry = alloc_entry (table);
+
+ return (DBusPreallocatedHash*) entry;
+}
+
+/**
+ * Frees an opaque DBusPreallocatedHash that was *not* used
+ * in order to insert into the hash table.
+ *
+ * @param table the hash table
+ * @param preallocated the preallocated data
+ */
+void
+_dbus_hash_table_free_preallocated_entry (DBusHashTable *table,
+ DBusPreallocatedHash *preallocated)
+{
+ DBusHashEntry *entry;
+
+ _dbus_assert (preallocated != NULL);
+
+ entry = (DBusHashEntry*) preallocated;
+
+ /* Don't use free_entry(), since this entry has no key/data */
+ _dbus_mem_pool_dealloc (table->entry_pool, entry);
+}
+
+/**
+ * Inserts a string-keyed entry into the hash table, using a
+ * preallocated data block from
+ * _dbus_hash_table_preallocate_entry(). This function cannot fail due
+ * to lack of memory. The DBusPreallocatedHash object is consumed and
+ * should not be reused or freed. Otherwise this function works
+ * just like _dbus_hash_table_insert_string().
+ *
+ * @param table the hash table
+ * @param preallocated the preallocated data
+ * @param key the hash key
+ * @param value the value
+ */
+void
+_dbus_hash_table_insert_string_preallocated (DBusHashTable *table,
+ DBusPreallocatedHash *preallocated,
+ char *key,
+ void *value)
+{
+ DBusHashEntry *entry;
+
+ _dbus_assert (table->key_type == DBUS_HASH_STRING);
+ _dbus_assert (preallocated != NULL);
+
+ entry = (* table->find_function) (table, key, TRUE, NULL, preallocated);
+
+ _dbus_assert (entry != NULL);
+
+ if (table->free_key_function && entry->key != key)
+ (* table->free_key_function) (entry->key);
+
+ if (table->free_value_function && entry->value != value)
+ (* table->free_value_function) (entry->value);
+
+ entry->key = key;
+ entry->value = value;
+}
+
+/**
+ * Gets the number of hash entries in a hash table.
+ *
+ * @param table the hash table.
+ * @returns the number of entries in the table.
+ */
+int
+_dbus_hash_table_get_n_entries (DBusHashTable *table)
+{
+ return table->n_entries;
+}
+
+/**
+ * Imports a string array into a hash table
+ * The hash table needs to be initialized with string keys,
+ * and dbus_free() as both key and value free-function.
+ *
+ * @param table the hash table
+ * @param array the string array to import
+ * @param delimiter the delimiter to separate key and value
+ * @return #TRUE on success.
+ * @return #FALSE if not enough memory.
+ */
+
+dbus_bool_t
+_dbus_hash_table_from_array (DBusHashTable *table, char **array, char delimiter)
+{
+ DBusString key;
+ DBusString value;
+ int i;
+ dbus_bool_t retval = FALSE;
+
+ _dbus_assert (table != NULL);
+ _dbus_assert (array != NULL);
+
+ if (!_dbus_string_init (&key))
+ {
+ return FALSE;
+ }
+
+ if (!_dbus_string_init (&value))
+ {
+ _dbus_string_free (&key);
+ return FALSE;
+ }
+
+ for (i = 0; array[i] != NULL; i++)
+ {
+ if (!_dbus_string_append (&key, array[i]))
+ break;
+
+ if (_dbus_string_split_on_byte (&key, delimiter, &value))
+ {
+ char *hash_key, *hash_value;
+
+ if (!_dbus_string_steal_data (&key, &hash_key))
+ break;
+
+ if (!_dbus_string_steal_data (&value, &hash_value))
+ break;
+
+ if (!_dbus_hash_table_insert_string (table,
+ hash_key, hash_value))
+ break;
+ }
+ _dbus_string_set_length (&key, 0);
+ _dbus_string_set_length (&value, 0);
+ }
+
+ if (array[i] != NULL)
+ goto out;
+
+ retval = TRUE;
+out:
+
+ _dbus_string_free (&key);
+ _dbus_string_free (&value);
+
+ return retval;
+}
+
+/**
+ * Creates a string array from a hash table
+ *
+ * @param table the hash table
+ * @param delimiter the delimiter to join key and value
+ * @return pointer to created string array (free with dbus_free_string_array)
+ * @return #FALSE if not enough memory.
+ */
+char **
+_dbus_hash_table_to_array (DBusHashTable *table, char delimiter)
+{
+ int i, length;
+ DBusString entry;
+ DBusHashIter iter;
+ char **array;
+
+ _dbus_assert (table != NULL);
+
+ length = _dbus_hash_table_get_n_entries (table);
+
+ array = dbus_new0 (char *, length + 1);
+
+ if (array == NULL)
+ return NULL;
+
+ i = 0;
+ _dbus_hash_iter_init (table, &iter);
+
+ if (!_dbus_string_init (&entry))
+ {
+ dbus_free_string_array (array);
+ return NULL;
+ }
+
+ while (_dbus_hash_iter_next (&iter))
+ {
+ const char *key, *value;
+
+ key = (const char *) _dbus_hash_iter_get_string_key (&iter);
+ value = (const char *) _dbus_hash_iter_get_value (&iter);
+
+ if (!_dbus_string_append_printf (&entry, "%s%c%s", key, delimiter, value))
+ break;
+
+ if (!_dbus_string_steal_data (&entry, array + i))
+ break;
+ i++;
+ }
+
+ _dbus_string_free (&entry);
+
+ if (i != length)
+ {
+ dbus_free_string_array (array);
+ array = NULL;
+ }
+
+ return array;
+}
+
+/** @} */
diff --git a/src/3rdparty/libdbus/dbus/dbus-hash.h b/src/3rdparty/libdbus/dbus/dbus-hash.h
new file mode 100644
index 00000000..290fb1a2
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-hash.h
@@ -0,0 +1,226 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-hash.h Generic hash table utility (internal to D-Bus implementation)
+ *
+ * Copyright (C) 2002 Red Hat, Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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
+ *
+ */
+
+#ifndef DBUS_HASH_H
+#define DBUS_HASH_H
+
+#include <stdint.h>
+
+#ifdef HAVE_INTTYPES_H
+#include <inttypes.h>
+#endif
+
+#include <dbus/dbus-memory.h>
+#include <dbus/dbus-types.h>
+#include <dbus/dbus-sysdeps.h>
+
+DBUS_BEGIN_DECLS
+
+/**
+ * @addtogroup DBusHashTable
+ * @{
+ */
+
+/** Hash iterator object. The iterator is on the stack, but its real
+ * fields are hidden privately.
+ */
+struct DBusHashIter
+{
+ void *dummy1; /**< Do not use. */
+ void *dummy2; /**< Do not use. */
+ void *dummy3; /**< Do not use. */
+ void *dummy4; /**< Do not use. */
+ int dummy5; /**< Do not use. */
+ int dummy6; /**< Do not use. */
+};
+
+typedef struct DBusHashTable DBusHashTable;
+typedef struct DBusHashIter DBusHashIter;
+
+/* Allowing an arbitrary function as with GLib
+ * would be nicer for a public API, but for
+ * an internal API this saves typing, we can add
+ * more whenever we feel like it.
+ */
+typedef enum
+{
+ DBUS_HASH_STRING, /**< Hash keys are strings. */
+ DBUS_HASH_INT, /**< Hash keys are integers. */
+ DBUS_HASH_UINTPTR /**< Hash keys are integer capable to hold a pointer. */
+} DBusHashType;
+
+DBUS_PRIVATE_EXPORT
+DBusHashTable* _dbus_hash_table_new (DBusHashType type,
+ DBusFreeFunction key_free_function,
+ DBusFreeFunction value_free_function);
+DBUS_PRIVATE_EXPORT
+DBusHashTable* _dbus_hash_table_ref (DBusHashTable *table);
+DBUS_PRIVATE_EXPORT
+void _dbus_hash_table_unref (DBusHashTable *table);
+void _dbus_hash_table_remove_all (DBusHashTable *table);
+DBUS_PRIVATE_EXPORT
+void _dbus_hash_iter_init (DBusHashTable *table,
+ DBusHashIter *iter);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_hash_iter_next (DBusHashIter *iter);
+DBUS_PRIVATE_EXPORT
+void _dbus_hash_iter_remove_entry (DBusHashIter *iter);
+DBUS_PRIVATE_EXPORT
+void* _dbus_hash_iter_get_value (DBusHashIter *iter);
+DBUS_PRIVATE_EXPORT
+void _dbus_hash_iter_set_value (DBusHashIter *iter,
+ void *value);
+DBUS_PRIVATE_EXPORT
+int _dbus_hash_iter_get_int_key (DBusHashIter *iter);
+DBUS_PRIVATE_EXPORT
+const char* _dbus_hash_iter_get_string_key (DBusHashIter *iter);
+DBUS_PRIVATE_EXPORT
+uintptr_t _dbus_hash_iter_get_uintptr_key (DBusHashIter *iter);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_hash_iter_lookup (DBusHashTable *table,
+ void *key,
+ dbus_bool_t create_if_not_found,
+ DBusHashIter *iter);
+DBUS_PRIVATE_EXPORT
+void* _dbus_hash_table_lookup_string (DBusHashTable *table,
+ const char *key);
+DBUS_PRIVATE_EXPORT
+void* _dbus_hash_table_lookup_int (DBusHashTable *table,
+ int key);
+DBUS_PRIVATE_EXPORT
+void* _dbus_hash_table_lookup_uintptr (DBusHashTable *table,
+ uintptr_t key);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_hash_table_remove_string (DBusHashTable *table,
+ const char *key);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_hash_table_remove_int (DBusHashTable *table,
+ int key);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_hash_table_remove_uintptr (DBusHashTable *table,
+ uintptr_t key);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_hash_table_insert_string (DBusHashTable *table,
+ char *key,
+ void *value);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_hash_table_insert_int (DBusHashTable *table,
+ int key,
+ void *value);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_hash_table_insert_uintptr (DBusHashTable *table,
+ uintptr_t key,
+ void *value);
+DBUS_PRIVATE_EXPORT
+int _dbus_hash_table_get_n_entries (DBusHashTable *table);
+
+DBUS_PRIVATE_EXPORT
+char ** _dbus_hash_table_to_array (DBusHashTable *table,
+ char delimiter);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_hash_table_from_array (DBusHashTable *table,
+ char **array,
+ char delimiter);
+
+/* Preallocation */
+
+/** A preallocated hash entry */
+typedef struct DBusPreallocatedHash DBusPreallocatedHash;
+
+DBUS_PRIVATE_EXPORT
+DBusPreallocatedHash *_dbus_hash_table_preallocate_entry (DBusHashTable *table);
+DBUS_PRIVATE_EXPORT
+void _dbus_hash_table_free_preallocated_entry (DBusHashTable *table,
+ DBusPreallocatedHash *preallocated);
+DBUS_PRIVATE_EXPORT
+void _dbus_hash_table_insert_string_preallocated (DBusHashTable *table,
+ DBusPreallocatedHash *preallocated,
+ char *key,
+ void *value);
+
+#ifdef DBUS_WIN
+# define DBUS_HASH_POLLABLE DBUS_HASH_UINTPTR
+#else
+# define DBUS_HASH_POLLABLE DBUS_HASH_INT
+#endif
+
+static inline DBusPollable
+_dbus_hash_iter_get_pollable_key (DBusHashIter *iter)
+{
+#ifdef DBUS_WIN
+ DBusSocket s;
+
+ s.sock = _dbus_hash_iter_get_uintptr_key (iter);
+ return s;
+#else
+ return _dbus_hash_iter_get_int_key (iter);
+#endif
+}
+
+static inline void *
+_dbus_hash_table_lookup_pollable (DBusHashTable *table,
+ DBusPollable key)
+{
+#ifdef DBUS_WIN
+ return _dbus_hash_table_lookup_uintptr (table, key.sock);
+#else
+ return _dbus_hash_table_lookup_int (table, key);
+#endif
+}
+
+static inline dbus_bool_t
+_dbus_hash_table_remove_pollable (DBusHashTable *table,
+ DBusPollable key)
+{
+#ifdef DBUS_WIN
+ return _dbus_hash_table_remove_uintptr (table, key.sock);
+#else
+ return _dbus_hash_table_remove_int (table, key);
+#endif
+}
+
+static inline dbus_bool_t
+_dbus_hash_table_insert_pollable (DBusHashTable *table,
+ DBusPollable key,
+ void *value)
+{
+#ifdef DBUS_WIN
+ return _dbus_hash_table_insert_uintptr (table, key.sock, value);
+#else
+ return _dbus_hash_table_insert_int (table, key, value);
+#endif
+}
+
+static inline void
+_dbus_clear_hash_table (DBusHashTable **table_p)
+{
+ _dbus_clear_pointer_impl (DBusHashTable, table_p, _dbus_hash_table_unref);
+}
+
+/** @} */
+
+DBUS_END_DECLS
+
+#endif /* DBUS_HASH_H */
diff --git a/src/3rdparty/libdbus/dbus/dbus-init-win.cpp b/src/3rdparty/libdbus/dbus/dbus-init-win.cpp
new file mode 100644
index 00000000..56311d8a
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-init-win.cpp
@@ -0,0 +1,54 @@
+/*
+ * dbus-init-win.cpp - once-per-process initialization
+ *
+ * Copyright © 2013 Intel Corporation
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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>
+
+extern "C"
+{
+#include "dbus-init-win.h"
+}
+
+class DBusInternalInit
+ {
+ public:
+ DBusInternalInit ()
+ {
+ _dbus_threads_windows_init_global ();
+ }
+
+ void must_not_be_omitted ()
+ {
+ }
+ };
+
+static DBusInternalInit init;
+
+extern "C" void
+_dbus_threads_windows_ensure_ctor_linked ()
+{
+ /* Do nothing significant, just ensure that the global initializer gets
+ * linked in. */
+ init.must_not_be_omitted ();
+}
diff --git a/src/3rdparty/libdbus/dbus/dbus-init-win.h b/src/3rdparty/libdbus/dbus/dbus-init-win.h
new file mode 100644
index 00000000..8a9de8f7
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-init-win.h
@@ -0,0 +1,17 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * Copyright © 2013 Intel Corporation
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * Do not include other private headers in this one, particularly
+ * dbus-sysdeps.h: it gets included into C++ code which is not
+ * compatible with our use of <stdatomic.h>.
+ */
+
+#ifndef DBUS_INIT_WIN_H
+#define DBUS_INIT_WIN_H
+
+void _dbus_threads_windows_init_global (void);
+void _dbus_threads_windows_ensure_ctor_linked (void);
+
+#endif
diff --git a/src/3rdparty/libdbus/dbus/dbus-internals.c b/src/3rdparty/libdbus/dbus/dbus-internals.c
new file mode 100644
index 00000000..6a23ff5b
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-internals.c
@@ -0,0 +1,1194 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-internals.c random utility stuff (internal to D-Bus implementation)
+ *
+ * Copyright (C) 2002, 2003 Red Hat, Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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-protocol.h"
+#include "dbus-marshal-basic.h"
+#include "dbus-test.h"
+#include "dbus-test-tap.h"
+#include "dbus-valgrind-internal.h"
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stdlib.h>
+#ifdef DBUS_USE_OUTPUT_DEBUG_STRING
+#include <windows.h>
+#include <mbstring.h>
+#endif
+
+/**
+ * @defgroup DBusInternals D-Bus secret internal implementation details
+ * @brief Documentation useful when developing or debugging D-Bus itself.
+ *
+ */
+
+/**
+ * @defgroup DBusInternalsUtils Utilities and portability
+ * @ingroup DBusInternals
+ * @brief Utility functions (_dbus_assert(), _dbus_warn(), etc.)
+ * @{
+ */
+
+/**
+ * @def _dbus_assert
+ *
+ * Aborts with an error message if the condition is false.
+ *
+ * @param condition condition which must be true.
+ */
+
+/**
+ * @def _dbus_assert_not_reached
+ *
+ * Aborts with an error message if called.
+ * The given explanation will be printed.
+ *
+ * @param explanation explanation of what happened if the code was reached.
+ */
+
+/**
+ * @def _DBUS_N_ELEMENTS
+ *
+ * Computes the number of elements in a fixed-size array using
+ * sizeof().
+ *
+ * @param array the array to count elements in.
+ */
+
+/**
+ * @def _DBUS_POINTER_TO_INT
+ *
+ * Safely casts a void* to an integer; should only be used on void*
+ * that actually contain integers, for example one created with
+ * _DBUS_INT_TO_POINTER. Only guaranteed to preserve 32 bits.
+ * (i.e. it's used to store 32-bit ints in pointers, but
+ * can't be used to store 64-bit pointers in ints.)
+ *
+ * @param pointer pointer to extract an integer from.
+ */
+/**
+ * @def _DBUS_INT_TO_POINTER
+ *
+ * Safely stuffs an integer into a pointer, to be extracted later with
+ * _DBUS_POINTER_TO_INT. Only guaranteed to preserve 32 bits.
+ *
+ * @param integer the integer to stuff into a pointer.
+ */
+/**
+ * @def _DBUS_ZERO
+ *
+ * Sets all bits in an object to zero.
+ *
+ * @param object the object to be zeroed.
+ */
+/**
+ * @def _DBUS_INT16_MIN
+ *
+ * Minimum value of type "int16"
+ */
+/**
+ * @def _DBUS_INT16_MAX
+ *
+ * Maximum value of type "int16"
+ */
+/**
+ * @def _DBUS_UINT16_MAX
+ *
+ * Maximum value of type "uint16"
+ */
+
+/**
+ * @def _DBUS_INT32_MIN
+ *
+ * Minimum value of type "int32"
+ */
+/**
+ * @def _DBUS_INT32_MAX
+ *
+ * Maximum value of type "int32"
+ */
+/**
+ * @def _DBUS_UINT32_MAX
+ *
+ * Maximum value of type "uint32"
+ */
+
+/**
+ * @def _DBUS_INT_MIN
+ *
+ * Minimum value of type "int"
+ */
+/**
+ * @def _DBUS_INT_MAX
+ *
+ * Maximum value of type "int"
+ */
+/**
+ * @def _DBUS_UINT_MAX
+ *
+ * Maximum value of type "uint"
+ */
+
+/**
+ * @typedef DBusForeachFunction
+ *
+ * Used to iterate over each item in a collection, such as
+ * a DBusList.
+ */
+
+/**
+ * @def _DBUS_LOCK_NAME
+ *
+ * Expands to name of a global lock variable.
+ */
+
+/**
+ * @def _DBUS_LOCK
+ *
+ * Locks a global lock, initializing it first if necessary.
+ *
+ * @returns #FALSE if not enough memory
+ */
+
+/**
+ * @def _DBUS_UNLOCK
+ *
+ * Unlocks a global lock
+ */
+
+/* The build system should have checked for DBUS_SIZEOF_VOID_P */
+_DBUS_STATIC_ASSERT (sizeof (void *) == DBUS_SIZEOF_VOID_P);
+
+/* dbus currently assumes that function pointers are essentially
+ * interchangeable with data pointers. There's nothing special about
+ * DBusShutdownFunction, it's just an arbitrary function pointer type.
+ * If this assertion fails on your platform, some porting will be required. */
+_DBUS_STATIC_ASSERT (sizeof (void *) == sizeof (DBusShutdownFunction));
+_DBUS_STATIC_ASSERT (_DBUS_ALIGNOF (void *) == _DBUS_ALIGNOF (DBusShutdownFunction));
+
+/* This is meant to be true by definition. */
+_DBUS_STATIC_ASSERT (sizeof (void *) == sizeof (intptr_t));
+_DBUS_STATIC_ASSERT (sizeof (void *) == sizeof (uintptr_t));
+
+/*
+ * Some frequent assumptions that we should *avoid* making include these,
+ * all of which are false on CHERI (which has 128-bit tagged pointers,
+ * but a 64-bit address space and therefore 64-bit sizes):
+ *
+ * sizeof (void *) <= sizeof (size_t)
+ * sizeof (void *) <= 8
+ * _DBUS_ALIGNOF (void *) <= 8
+ *
+ * We should also avoid making these assumptions, although we don't currently
+ * know a concrete example of platforms where they're false:
+ *
+ * sizeof (ptrdiff_t) == sizeof (size_t)
+ */
+
+/**
+ * Fixed "out of memory" error message, just to avoid
+ * making up a different string every time and wasting
+ * space.
+ */
+const char *_dbus_no_memory_message = "Not enough memory";
+
+/* Not necessarily thread-safe, but if writes don't propagate between
+ * threads, the worst that will happen is that we duplicate work in more than
+ * one thread. */
+static dbus_bool_t warn_initted = FALSE;
+
+/* Not necessarily thread-safe, but if writes don't propagate between
+ * threads, the worst that will happen is that warnings get their default
+ * fatal/non-fatal nature. */
+static dbus_bool_t fatal_warnings = FALSE;
+static dbus_bool_t fatal_warnings_on_check_failed = TRUE;
+
+static int check_failed_count = 0;
+
+int _dbus_get_check_failed_count (void)
+{
+ return check_failed_count;
+}
+
+static void
+init_warnings(void)
+{
+ if (!warn_initted)
+ {
+ const char *s;
+ s = _dbus_getenv ("DBUS_FATAL_WARNINGS");
+ if (s && *s)
+ {
+ if (*s == '0')
+ {
+ fatal_warnings = FALSE;
+ fatal_warnings_on_check_failed = FALSE;
+ }
+ else if (*s == '1')
+ {
+ fatal_warnings = TRUE;
+ fatal_warnings_on_check_failed = TRUE;
+ }
+ else
+ {
+ fprintf(stderr, "DBUS_FATAL_WARNINGS should be set to 0 or 1 if set, not '%s'",
+ s);
+ }
+ }
+
+ check_failed_count = 0;
+
+ warn_initted = TRUE;
+ }
+}
+
+/**
+ * Prints a warning message to stderr. Can optionally be made to exit
+ * fatally by setting DBUS_FATAL_WARNINGS, but this is rarely
+ * used. This function should be considered pretty much equivalent to
+ * fprintf(stderr). _dbus_warn_check_failed() on the other hand is
+ * suitable for use when a programming mistake has been made.
+ *
+ * @param format printf-style format string.
+ */
+void
+_dbus_warn (const char *format,
+ ...)
+{
+ DBusSystemLogSeverity severity = DBUS_SYSTEM_LOG_WARNING;
+ va_list args;
+
+ if (!warn_initted)
+ init_warnings ();
+
+ if (fatal_warnings)
+ severity = DBUS_SYSTEM_LOG_ERROR;
+
+ va_start (args, format);
+ _dbus_logv (severity, format, args);
+ va_end (args);
+
+ if (fatal_warnings)
+ {
+ fflush (stderr);
+ _dbus_abort ();
+ }
+}
+
+/**
+ * Prints a "critical" warning to stderr when an assertion fails;
+ * differs from _dbus_warn primarily in that it
+ * defaults to fatal. This should be used only when a programming
+ * error has been detected. (NOT for unavoidable errors that an app
+ * might handle - those should be returned as DBusError.) Calling this
+ * means "there is a bug"
+ */
+void
+_dbus_warn_check_failed(const char *format,
+ ...)
+{
+ DBusSystemLogSeverity severity = DBUS_SYSTEM_LOG_WARNING;
+ va_list args;
+
+ if (!warn_initted)
+ init_warnings ();
+
+ if (fatal_warnings_on_check_failed)
+ severity = DBUS_SYSTEM_LOG_ERROR;
+
+ va_start (args, format);
+ _dbus_logv (severity, format, args);
+ va_end (args);
+
+ if (fatal_warnings_on_check_failed)
+ {
+ fflush (stderr);
+ _dbus_abort ();
+ }
+ else
+ check_failed_count++;
+}
+
+#ifdef DBUS_ENABLE_VERBOSE_MODE
+
+static dbus_bool_t verbose_initted = FALSE;
+static dbus_bool_t verbose = TRUE;
+
+#ifdef DBUS_USE_OUTPUT_DEBUG_STRING
+static char module_name[1024];
+#endif
+
+static inline void
+_dbus_verbose_init (void)
+{
+ if (!verbose_initted)
+ {
+ const char *p = _dbus_getenv ("DBUS_VERBOSE");
+ verbose = p != NULL && *p == '1';
+ verbose_initted = TRUE;
+#ifdef DBUS_USE_OUTPUT_DEBUG_STRING
+ {
+ char *last_period, *last_slash;
+ GetModuleFileName(0,module_name,sizeof(module_name)-1);
+ last_period = _mbsrchr(module_name,'.');
+ if (last_period)
+ *last_period ='\0';
+ last_slash = _mbsrchr(module_name,'\\');
+ if (last_slash)
+ strcpy(module_name,last_slash+1);
+ strcat(module_name,": ");
+ }
+#endif
+ }
+}
+
+/**
+ remove source root from file path
+ the source root is determined by
+*/
+static char *_dbus_file_path_extract_elements_from_tail(const char *file,int level)
+{
+ int prefix = 0;
+ char *p = (char *)file + strlen(file);
+ int i = 0;
+
+ for (;p >= file;p--)
+ {
+ if (DBUS_IS_DIR_SEPARATOR(*p))
+ {
+ if (++i >= level)
+ {
+ prefix = p-file+1;
+ break;
+ }
+ }
+ }
+
+ return (char *)file+prefix;
+}
+
+/**
+ * Implementation of dbus_is_verbose() macro if built with verbose logging
+ * enabled.
+ * @returns whether verbose logging is active.
+ */
+dbus_bool_t
+_dbus_is_verbose_real (void)
+{
+ _dbus_verbose_init ();
+ return verbose;
+}
+
+void _dbus_set_verbose (dbus_bool_t state)
+{
+ verbose = state;
+}
+
+dbus_bool_t _dbus_get_verbose (void)
+{
+ return verbose;
+}
+
+/**
+ * Low-level function for displaying a string
+ * for the predefined output channel, which
+ * can be the Windows debug output port or stderr.
+ *
+ * This function must be used instead of
+ * dbus_verbose(), if a dynamic memory request
+ * cannot be used to avoid recursive call loops.
+ *
+ * @param s string to display
+ */
+void
+_dbus_verbose_raw (const char *s)
+{
+ if (!_dbus_is_verbose_real ())
+ return;
+#ifdef DBUS_USE_OUTPUT_DEBUG_STRING
+ OutputDebugStringA (s);
+#else
+ fputs (s, stderr);
+ fflush (stderr);
+#endif
+}
+
+/**
+ * Prints a warning message to stderr
+ * if the user has enabled verbose mode.
+ * This is the real function implementation,
+ * use _dbus_verbose() macro in code.
+ *
+ * @param format printf-style format string.
+ */
+void
+_dbus_verbose_real (
+#ifdef DBUS_CPP_SUPPORTS_VARIABLE_MACRO_ARGUMENTS
+ const char *file,
+ const int line,
+ const char *function,
+#endif
+ const char *format,
+ ...)
+{
+ va_list args;
+ static dbus_bool_t need_pid = TRUE;
+ int len;
+ dbus_int64_t sec;
+ long usec;
+
+ /* things are written a bit oddly here so that
+ * in the non-verbose case we just have the one
+ * conditional and return immediately.
+ */
+ if (!_dbus_is_verbose_real())
+ return;
+
+#ifndef DBUS_USE_OUTPUT_DEBUG_STRING
+ /* Print out pid before the line */
+ if (need_pid)
+ {
+ _dbus_print_thread ();
+ }
+ _dbus_get_real_time (&sec, &usec);
+ fprintf (stderr, "%" DBUS_INT64_MODIFIER "d.%06ld ", sec, usec);
+#endif
+
+ /* Only print pid again if the next line is a new line */
+ len = strlen (format);
+ if (format[len-1] == '\n')
+ need_pid = TRUE;
+ else
+ need_pid = FALSE;
+
+ va_start (args, format);
+
+#ifdef DBUS_USE_OUTPUT_DEBUG_STRING
+ {
+ DBusString out = _DBUS_STRING_INIT_INVALID;
+ const char *message = NULL;
+
+ if (!_dbus_string_init (&out))
+ goto out;
+
+ if (!_dbus_string_append (&out, module_name))
+ goto out;
+
+#ifdef DBUS_CPP_SUPPORTS_VARIABLE_MACRO_ARGUMENTS
+ if (!_dbus_string_append_printf (&out, "[%s(%d):%s] ", _dbus_file_path_extract_elements_from_tail (file, 2), line, function))
+ goto out;
+#endif
+ if (!_dbus_string_append_printf_valist (&out, format, args))
+ goto out;
+ message = _dbus_string_get_const_data (&out);
+out:
+ if (message == NULL)
+ {
+ OutputDebugStringA ("Out of memory while formatting verbose message: '''");
+ OutputDebugStringA (format);
+ OutputDebugStringA ("'''");
+ }
+ else
+ {
+ OutputDebugStringA (message);
+ }
+ _dbus_string_free (&out);
+ }
+#else
+#ifdef DBUS_CPP_SUPPORTS_VARIABLE_MACRO_ARGUMENTS
+ fprintf (stderr, "[%s(%d):%s] ",_dbus_file_path_extract_elements_from_tail(file,2),line,function);
+#endif
+
+ vfprintf (stderr, format, args);
+ fflush (stderr);
+#endif
+
+ va_end (args);
+}
+
+/**
+ * Reinitializes the verbose logging code, used
+ * as a hack in dbus-spawn-unix.c so that a child
+ * process re-reads its pid
+ *
+ */
+void
+_dbus_verbose_reset_real (void)
+{
+ verbose_initted = FALSE;
+}
+
+void
+_dbus_trace_ref (const char *obj_name,
+ void *obj,
+ int old_refcount,
+ int new_refcount,
+ const char *why,
+ const char *env_var,
+ int *enabled)
+{
+ _dbus_assert (obj_name != NULL);
+ _dbus_assert (obj != NULL);
+ _dbus_assert (old_refcount >= -1);
+ _dbus_assert (new_refcount >= -1);
+
+ if (old_refcount == -1)
+ {
+ _dbus_assert (new_refcount == -1);
+ }
+ else
+ {
+ _dbus_assert (new_refcount >= 0);
+ _dbus_assert (old_refcount >= 0);
+ _dbus_assert (old_refcount > 0 || new_refcount > 0);
+ }
+
+ _dbus_assert (why != NULL);
+ _dbus_assert (env_var != NULL);
+ _dbus_assert (enabled != NULL);
+
+ if (*enabled < 0)
+ {
+ const char *s = _dbus_getenv (env_var);
+
+ *enabled = FALSE;
+
+ if (s && *s)
+ {
+ if (*s == '0')
+ *enabled = FALSE;
+ else if (*s == '1')
+ *enabled = TRUE;
+ else
+ _dbus_warn ("%s should be 0 or 1 if set, not '%s'", env_var, s);
+ }
+ }
+
+ if (*enabled)
+ {
+ if (old_refcount == -1)
+ {
+ VALGRIND_PRINTF_BACKTRACE ("%s %p ref stolen (%s)",
+ obj_name, obj, why);
+ _dbus_verbose ("%s %p ref stolen (%s)\n",
+ obj_name, obj, why);
+ }
+ else
+ {
+ VALGRIND_PRINTF_BACKTRACE ("%s %p %d -> %d refs (%s)",
+ obj_name, obj,
+ old_refcount, new_refcount, why);
+ _dbus_verbose ("%s %p %d -> %d refs (%s)\n",
+ obj_name, obj, old_refcount, new_refcount, why);
+ }
+ }
+}
+
+#endif /* DBUS_ENABLE_VERBOSE_MODE */
+
+/**
+ * Duplicates a string. Result must be freed with
+ * dbus_free(). Returns #NULL if memory allocation fails.
+ * If the string to be duplicated is #NULL, returns #NULL.
+ *
+ * @param str string to duplicate.
+ * @returns newly-allocated copy.
+ */
+char*
+_dbus_strdup (const char *str)
+{
+ size_t len;
+ char *copy;
+
+ if (str == NULL)
+ return NULL;
+
+ len = strlen (str);
+
+ copy = dbus_malloc (len + 1);
+ if (copy == NULL)
+ return NULL;
+
+ memcpy (copy, str, len + 1);
+
+ return copy;
+}
+
+/**
+ * Duplicates a block of memory. Returns
+ * #NULL on failure.
+ *
+ * @param mem memory to copy
+ * @param n_bytes number of bytes to copy
+ * @returns the copy
+ */
+void*
+_dbus_memdup (const void *mem,
+ size_t n_bytes)
+{
+ void *copy;
+
+ copy = dbus_malloc (n_bytes);
+ if (copy == NULL)
+ return NULL;
+
+ memcpy (copy, mem, n_bytes);
+
+ return copy;
+}
+
+/**
+ * Duplicates a string array. Result may be freed with
+ * dbus_free_string_array(). Returns #NULL if memory allocation fails.
+ * If the array to be duplicated is #NULL, returns #NULL.
+ *
+ * @param array array to duplicate.
+ * @returns newly-allocated copy.
+ */
+char**
+_dbus_dup_string_array (const char **array)
+{
+ int len;
+ int i;
+ char **copy;
+
+ if (array == NULL)
+ return NULL;
+
+ for (len = 0; array[len] != NULL; ++len)
+ ;
+
+ copy = dbus_new0 (char*, len + 1);
+ if (copy == NULL)
+ return NULL;
+
+ i = 0;
+ while (i < len)
+ {
+ copy[i] = _dbus_strdup (array[i]);
+ if (copy[i] == NULL)
+ {
+ dbus_free_string_array (copy);
+ return NULL;
+ }
+
+ ++i;
+ }
+
+ return copy;
+}
+
+/**
+ * Checks whether a string array contains the given string.
+ *
+ * @param array array to search.
+ * @param str string to look for
+ * @returns #TRUE if array contains string
+ */
+dbus_bool_t
+_dbus_string_array_contains (const char **array,
+ const char *str)
+{
+ int i;
+
+ i = 0;
+ while (array[i] != NULL)
+ {
+ if (strcmp (array[i], str) == 0)
+ return TRUE;
+ ++i;
+ }
+
+ return FALSE;
+}
+
+/**
+ * Returns the size of a string array
+ *
+ * @param array array to search.
+ * @returns size of array
+ */
+size_t
+_dbus_string_array_length (const char **array)
+{
+ size_t i;
+ for (i = 0; array[i]; i++) {}
+ return i;
+}
+
+
+/**
+ * Generates a new UUID. If you change how this is done,
+ * there's some text about it in the spec that should also change.
+ *
+ * @param uuid the uuid to initialize
+ * @param error location to store reason for failure
+ * @returns #TRUE on success
+ */
+dbus_bool_t
+_dbus_generate_uuid (DBusGUID *uuid,
+ DBusError *error)
+{
+ DBusError rand_error;
+ dbus_int64_t now;
+
+ dbus_error_init (&rand_error);
+
+ /* don't use monotonic time because the UUID may be saved to disk, e.g.
+ * it may persist across reboots
+ */
+ _dbus_get_real_time (&now, NULL);
+
+ uuid->as_uint32s[DBUS_UUID_LENGTH_WORDS - 1] = DBUS_UINT32_TO_BE (now);
+
+ if (!_dbus_generate_random_bytes_buffer (uuid->as_bytes,
+ DBUS_UUID_LENGTH_BYTES - 4,
+ &rand_error))
+ {
+ dbus_set_error (error, rand_error.name,
+ "Failed to generate UUID: %s", rand_error.message);
+ dbus_error_free (&rand_error);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**
+ * Hex-encode a UUID.
+ *
+ * @param uuid the uuid
+ * @param encoded string to append hex uuid to
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_uuid_encode (const DBusGUID *uuid,
+ DBusString *encoded)
+{
+ DBusString binary;
+ _dbus_string_init_const_len (&binary, uuid->as_bytes, DBUS_UUID_LENGTH_BYTES);
+ return _dbus_string_hex_encode (&binary, 0, encoded, _dbus_string_get_length (encoded));
+}
+
+static dbus_bool_t
+_dbus_read_uuid_file_without_creating (const DBusString *filename,
+ DBusGUID *uuid,
+ DBusError *error)
+{
+ DBusString contents;
+ DBusString decoded;
+ int end;
+
+ if (!_dbus_string_init (&contents))
+ {
+ _DBUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ if (!_dbus_string_init (&decoded))
+ {
+ _dbus_string_free (&contents);
+ _DBUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ if (!_dbus_file_get_contents (&contents, filename, error))
+ goto error;
+
+ _dbus_string_chop_white (&contents);
+
+ if (_dbus_string_get_length (&contents) != DBUS_UUID_LENGTH_HEX)
+ {
+ dbus_set_error (error, DBUS_ERROR_INVALID_FILE_CONTENT,
+ "UUID file '%s' should contain a hex string of length %d, not length %d, with no other text",
+ _dbus_string_get_const_data (filename),
+ DBUS_UUID_LENGTH_HEX,
+ _dbus_string_get_length (&contents));
+ goto error;
+ }
+
+ if (!_dbus_string_hex_decode (&contents, 0, &end, &decoded, 0))
+ {
+ _DBUS_SET_OOM (error);
+ goto error;
+ }
+
+ if (end == 0)
+ {
+ dbus_set_error (error, DBUS_ERROR_INVALID_FILE_CONTENT,
+ "UUID file '%s' contains invalid hex data",
+ _dbus_string_get_const_data (filename));
+ goto error;
+ }
+
+ if (_dbus_string_get_length (&decoded) != DBUS_UUID_LENGTH_BYTES)
+ {
+ dbus_set_error (error, DBUS_ERROR_INVALID_FILE_CONTENT,
+ "UUID file '%s' contains %d bytes of hex-encoded data instead of %d",
+ _dbus_string_get_const_data (filename),
+ _dbus_string_get_length (&decoded),
+ DBUS_UUID_LENGTH_BYTES);
+ goto error;
+ }
+
+ _dbus_string_copy_to_buffer (&decoded, uuid->as_bytes, DBUS_UUID_LENGTH_BYTES);
+
+ _dbus_string_free (&decoded);
+ _dbus_string_free (&contents);
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ return TRUE;
+
+ error:
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ _dbus_string_free (&contents);
+ _dbus_string_free (&decoded);
+ return FALSE;
+}
+
+/**
+ * Write the give UUID to a file.
+ *
+ * @param filename the file to write
+ * @param uuid the UUID to save
+ * @param error used to raise an error
+ * @returns #FALSE on error
+ */
+dbus_bool_t
+_dbus_write_uuid_file (const DBusString *filename,
+ const DBusGUID *uuid,
+ DBusError *error)
+{
+ DBusString encoded;
+
+ if (!_dbus_string_init (&encoded))
+ {
+ _DBUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ if (!_dbus_uuid_encode (uuid, &encoded))
+ {
+ _DBUS_SET_OOM (error);
+ goto error;
+ }
+
+ if (!_dbus_string_append_byte (&encoded, '\n'))
+ {
+ _DBUS_SET_OOM (error);
+ goto error;
+ }
+
+ if (!_dbus_string_save_to_file (&encoded, filename, TRUE, error))
+ goto error;
+
+ _dbus_string_free (&encoded);
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+ return TRUE;
+
+ error:
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ _dbus_string_free (&encoded);
+ return FALSE;
+}
+
+/**
+ * Reads (and optionally writes) a uuid to a file. Initializes the uuid
+ * unless an error is returned.
+ *
+ * @param filename the name of the file
+ * @param uuid uuid to be initialized with the loaded uuid
+ * @param create_if_not_found #TRUE to create a new uuid and save it if the file doesn't exist
+ * @param error the error return
+ * @returns #FALSE if the error is set
+ */
+dbus_bool_t
+_dbus_read_uuid_file (const DBusString *filename,
+ DBusGUID *uuid,
+ dbus_bool_t create_if_not_found,
+ DBusError *error)
+{
+ DBusError read_error = DBUS_ERROR_INIT;
+
+ if (_dbus_read_uuid_file_without_creating (filename, uuid, &read_error))
+ return TRUE;
+
+ if (!create_if_not_found)
+ {
+ dbus_move_error (&read_error, error);
+ return FALSE;
+ }
+
+ /* If the file exists and contains junk, we want to keep that error
+ * message instead of overwriting it with a "file exists" error
+ * message when we try to write
+ */
+ if (dbus_error_has_name (&read_error, DBUS_ERROR_INVALID_FILE_CONTENT))
+ {
+ dbus_move_error (&read_error, error);
+ return FALSE;
+ }
+ else
+ {
+ dbus_error_free (&read_error);
+
+ if (!_dbus_generate_uuid (uuid, error))
+ return FALSE;
+
+ return _dbus_write_uuid_file (filename, uuid, error);
+ }
+}
+
+/* Protected by _DBUS_LOCK (machine_uuid) */
+static int machine_uuid_initialized_generation = 0;
+static DBusGUID machine_uuid;
+
+/**
+ * Gets the hex-encoded UUID of the machine this function is
+ * executed on. This UUID is guaranteed to be the same for a given
+ * machine at least until it next reboots, though it also
+ * makes some effort to be the same forever, it may change if the
+ * machine is reconfigured or its hardware is modified.
+ *
+ * @param uuid_str string to append hex-encoded machine uuid to
+ * @param error location to store reason for failure
+ * @returns #TRUE if successful
+ */
+dbus_bool_t
+_dbus_get_local_machine_uuid_encoded (DBusString *uuid_str,
+ DBusError *error)
+{
+ dbus_bool_t ok = TRUE;
+
+ if (!_DBUS_LOCK (machine_uuid))
+ {
+ _DBUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ if (machine_uuid_initialized_generation != _dbus_current_generation)
+ {
+ if (!_dbus_read_local_machine_uuid (&machine_uuid, FALSE, error))
+ ok = FALSE;
+ }
+
+ if (ok)
+ {
+ if (!_dbus_uuid_encode (&machine_uuid, uuid_str))
+ {
+ ok = FALSE;
+ _DBUS_SET_OOM (error);
+ }
+ }
+
+ _DBUS_UNLOCK (machine_uuid);
+
+ return ok;
+}
+
+#ifndef DBUS_DISABLE_CHECKS
+void
+_dbus_warn_return_if_fail (const char *function,
+ const char *assertion,
+ const char *file,
+ int line)
+{
+ _dbus_warn_check_failed (
+ "arguments to %s() were incorrect, assertion \"%s\" failed in file %s line %d.\n"
+ "This is normally a bug in some application using the D-Bus library.\n",
+ function, assertion, file, line);
+}
+#endif
+
+#ifndef DBUS_DISABLE_ASSERT
+/**
+ * Internals of _dbus_assert(); it's a function
+ * rather than a macro with the inline code so
+ * that the assertion failure blocks don't show up
+ * in test suite coverage, and to shrink code size.
+ *
+ * @param condition TRUE if assertion succeeded
+ * @param condition_text condition as a string
+ * @param file file the assertion is in
+ * @param line line the assertion is in
+ * @param func function the assertion is in
+ */
+void
+_dbus_real_assert (dbus_bool_t condition,
+ const char *condition_text,
+ const char *file,
+ int line,
+ const char *func)
+{
+ if (_DBUS_UNLIKELY (!condition))
+ {
+ _dbus_warn ("assertion failed \"%s\" file \"%s\" line %d function %s",
+ condition_text, file, line, func);
+ _dbus_abort ();
+ }
+}
+
+/**
+ * Internals of _dbus_assert_not_reached(); it's a function
+ * rather than a macro with the inline code so
+ * that the assertion failure blocks don't show up
+ * in test suite coverage, and to shrink code size.
+ *
+ * @param explanation what was reached that shouldn't have been
+ * @param file file the assertion is in
+ * @param line line the assertion is in
+ */
+void
+_dbus_real_assert_not_reached (const char *explanation,
+ const char *file,
+ int line)
+{
+ _dbus_warn ("File \"%s\" line %d should not have been reached: %s",
+ file, line, explanation);
+ _dbus_abort ();
+}
+#endif /* DBUS_DISABLE_ASSERT */
+
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
+static dbus_bool_t
+run_failing_each_malloc (int n_mallocs,
+ const char *description,
+ DBusTestMemoryFunction func,
+ void *data)
+{
+ n_mallocs += 10; /* fudge factor to ensure reallocs etc. are covered */
+
+ while (n_mallocs >= 0)
+ {
+ _dbus_set_fail_alloc_counter (n_mallocs);
+
+ _dbus_test_diag ("%s: will fail malloc %d and %d that follow",
+ description, n_mallocs,
+ _dbus_get_fail_alloc_failures () - 1);
+
+ if (!(* func) (data, FALSE))
+ return FALSE;
+
+ n_mallocs -= 1;
+ }
+
+ _dbus_set_fail_alloc_counter (_DBUS_INT_MAX);
+
+ return TRUE;
+}
+
+/**
+ * Tests how well the given function responds to out-of-memory
+ * situations. Calls the function repeatedly, failing a different
+ * call to malloc() each time. If the function ever returns #FALSE,
+ * the test fails. The function should return #TRUE whenever something
+ * valid (such as returning an error, or succeeding) occurs, and #FALSE
+ * if it gets confused in some way.
+ *
+ * @param description description of the test used in verbose output
+ * @param func function to call
+ * @param data data to pass to function
+ * @returns #TRUE if the function never returns FALSE
+ */
+dbus_bool_t
+_dbus_test_oom_handling (const char *description,
+ DBusTestMemoryFunction func,
+ void *data)
+{
+ int approx_mallocs;
+ const char *setting;
+ int max_failures_to_try;
+ int i;
+
+ /* Run once to see about how many mallocs are involved */
+
+ _dbus_set_fail_alloc_counter (_DBUS_INT_MAX);
+
+ _dbus_test_diag ("Running \"%s\" once to count mallocs", description);
+
+ if (!(* func) (data, TRUE))
+ return FALSE;
+
+ approx_mallocs = _DBUS_INT_MAX - _dbus_get_fail_alloc_counter ();
+
+ _dbus_test_diag ("\"%s\" has about %d mallocs in total",
+ description, approx_mallocs);
+
+ setting = _dbus_getenv ("DBUS_TEST_MALLOC_FAILURES");
+
+ if (setting != NULL)
+ {
+ DBusString str;
+ long v;
+ _dbus_string_init_const (&str, setting);
+ v = 4;
+ if (!_dbus_string_parse_int (&str, 0, &v, NULL))
+ _dbus_warn ("couldn't parse '%s' as integer\n", setting);
+ max_failures_to_try = v;
+ }
+ else
+ {
+ max_failures_to_try = 4;
+ }
+
+ if (RUNNING_ON_VALGRIND && _dbus_getenv ("DBUS_TEST_SLOW") == NULL)
+ {
+ /* The full OOM testing is slow, valgrind is slow, so the
+ * combination is just horrible. Only do this if the user
+ * asked for extra-slow tests. */
+ max_failures_to_try = 0;
+ }
+
+ if (max_failures_to_try < 1)
+ {
+ _dbus_test_diag ("not testing OOM handling");
+ return TRUE;
+ }
+
+ _dbus_test_diag ("testing \"%s\" with up to %d consecutive malloc failures",
+ description, max_failures_to_try);
+
+ i = setting ? max_failures_to_try - 1 : 1;
+ while (i < max_failures_to_try)
+ {
+ _dbus_test_diag ("testing \"%s\" with %d consecutive malloc failures",
+ description, i + 1);
+
+ _dbus_set_fail_alloc_failures (i);
+ if (!run_failing_each_malloc (approx_mallocs, description, func, data))
+ return FALSE;
+ ++i;
+ }
+
+ _dbus_verbose ("\"%s\" coped OK with malloc failures\n", description);
+
+ return TRUE;
+}
+#endif /* DBUS_ENABLE_EMBEDDED_TESTS */
+
+/** @} */
diff --git a/src/3rdparty/libdbus/dbus/dbus-internals.h b/src/3rdparty/libdbus/dbus/dbus-internals.h
new file mode 100644
index 00000000..6266bb0d
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-internals.h
@@ -0,0 +1,528 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-internals.h random utility stuff (internal to D-Bus implementation)
+ *
+ * Copyright (C) 2002, 2003 Red Hat, Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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
+ *
+ */
+#ifdef DBUS_INSIDE_DBUS_H
+#error "You can't include dbus-internals.h in the public header dbus.h"
+#endif
+
+#ifndef DBUS_INTERNALS_H
+#define DBUS_INTERNALS_H
+
+#include <dbus/dbus-memory.h>
+#include <dbus/dbus-types.h>
+#include <dbus/dbus-errors.h>
+#include <dbus/dbus-sysdeps.h>
+#include <dbus/dbus-macros-internal.h>
+#include <dbus/dbus-threads-internal.h>
+
+DBUS_BEGIN_DECLS
+
+DBUS_PRIVATE_EXPORT
+void _dbus_warn (const char *format,
+ ...) _DBUS_GNUC_PRINTF (1, 2);
+
+DBUS_PRIVATE_EXPORT
+void _dbus_warn_check_failed (const char *format,
+ ...) _DBUS_GNUC_PRINTF (1, 2);
+DBUS_PRIVATE_EXPORT
+void _dbus_warn_return_if_fail (const char *function,
+ const char *assertion,
+ const char *file,
+ int line);
+
+DBUS_EMBEDDED_TESTS_EXPORT
+int _dbus_get_check_failed_count (void);
+
+#if defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
+#define _DBUS_FUNCTION_NAME __func__
+#elif defined(__GNUC__) || defined(_MSC_VER)
+#define _DBUS_FUNCTION_NAME __FUNCTION__
+#else
+#define _DBUS_FUNCTION_NAME "unknown function"
+#endif
+
+/*
+ * (code from GLib)
+ *
+ * The _DBUS_LIKELY and _DBUS_UNLIKELY macros let the programmer give hints to
+ * the compiler about the expected result of an expression. Some compilers
+ * can use this information for optimizations.
+ *
+ * The _DBUS_BOOLEAN_EXPR macro is intended to trigger a gcc warning when
+ * putting assignments in the macro arg
+ */
+#if defined(__GNUC__) && (__GNUC__ > 2) && defined(__OPTIMIZE__)
+#define _DBUS_BOOLEAN_EXPR(expr) \
+ __extension__ ({ \
+ int _dbus_boolean_var_; \
+ if (expr) \
+ _dbus_boolean_var_ = 1; \
+ else \
+ _dbus_boolean_var_ = 0; \
+ _dbus_boolean_var_; \
+})
+#define _DBUS_LIKELY(expr) (__builtin_expect (_DBUS_BOOLEAN_EXPR(expr), 1))
+#define _DBUS_UNLIKELY(expr) (__builtin_expect (_DBUS_BOOLEAN_EXPR(expr), 0))
+#else
+#define _DBUS_LIKELY(expr) (expr)
+#define _DBUS_UNLIKELY(expr) (expr)
+#endif
+
+#ifdef DBUS_ENABLE_VERBOSE_MODE
+
+/*
+ at least gnu cc and msvc compiler are known to
+ have support for variable macro argument lists
+ add other compilers is required
+*/
+#if defined(__GNUC__) || defined(_MSC_VER)
+#define DBUS_CPP_SUPPORTS_VARIABLE_MACRO_ARGUMENTS
+#endif
+
+#ifdef DBUS_CPP_SUPPORTS_VARIABLE_MACRO_ARGUMENTS
+DBUS_PRIVATE_EXPORT
+void _dbus_verbose_real (const char *file, const int line, const char *function,
+ const char *format,...) _DBUS_GNUC_PRINTF (4, 5);
+# define _dbus_verbose(fmt,...) _dbus_verbose_real( __FILE__,__LINE__,_DBUS_FUNCTION_NAME,fmt, ## __VA_ARGS__)
+#else
+DBUS_PRIVATE_EXPORT
+void _dbus_verbose_real (const char *format,
+ ...) _DBUS_GNUC_PRINTF (1, 2);
+# define _dbus_verbose _dbus_verbose_real
+#endif
+DBUS_PRIVATE_EXPORT
+void _dbus_verbose_reset_real (void);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_is_verbose_real (void);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_get_verbose (void);
+DBUS_PRIVATE_EXPORT
+void _dbus_set_verbose (dbus_bool_t state);
+void _dbus_verbose_raw (const char *s);
+
+# define _dbus_verbose_reset _dbus_verbose_reset_real
+# define _dbus_is_verbose _dbus_is_verbose_real
+#else
+# define _dbus_verbose(...) do { } while (0)
+# define _dbus_verbose_reset() do { } while (0)
+# define _dbus_is_verbose() FALSE
+#endif /* !DBUS_ENABLE_VERBOSE_MODE */
+
+DBUS_PRIVATE_EXPORT
+void _dbus_trace_ref (const char *obj_name,
+ void *obj,
+ int old_refcount,
+ int new_refcount,
+ const char *why,
+ const char *env_var,
+ int *enabled);
+
+DBUS_PRIVATE_EXPORT
+const char* _dbus_strerror (int error_number);
+
+#ifdef DBUS_DISABLE_ASSERT
+#define _dbus_assert(condition) do { } while (0)
+#else
+DBUS_PRIVATE_EXPORT
+void _dbus_real_assert (dbus_bool_t condition,
+ const char *condition_text,
+ const char *file,
+ int line,
+ const char *func);
+#define _dbus_assert(condition) \
+ _dbus_real_assert ((condition) != 0, #condition, __FILE__, __LINE__, _DBUS_FUNCTION_NAME)
+#endif /* !DBUS_DISABLE_ASSERT */
+
+#ifdef DBUS_DISABLE_ASSERT
+#define _dbus_assert_not_reached(explanation) do { } while (0)
+#else
+DBUS_PRIVATE_EXPORT
+void _dbus_real_assert_not_reached (const char *explanation,
+ const char *file,
+ int line) _DBUS_GNUC_NORETURN;
+#define _dbus_assert_not_reached(explanation) \
+ _dbus_real_assert_not_reached (explanation, __FILE__, __LINE__)
+#endif /* !DBUS_DISABLE_ASSERT */
+
+#ifdef DBUS_DISABLE_CHECKS
+#define _dbus_return_if_fail(condition)
+#define _dbus_return_val_if_fail(condition, val)
+#else
+
+#define _dbus_return_if_fail(condition) do { \
+ _dbus_assert ((*(const char*)_DBUS_FUNCTION_NAME) != '_'); \
+ if (!(condition)) { \
+ _dbus_warn_return_if_fail (_DBUS_FUNCTION_NAME, #condition, __FILE__, __LINE__); \
+ return; \
+ } } while (0)
+
+#define _dbus_return_val_if_fail(condition, val) do { \
+ _dbus_assert ((*(const char*)_DBUS_FUNCTION_NAME) != '_'); \
+ if (!(condition)) { \
+ _dbus_warn_return_if_fail (_DBUS_FUNCTION_NAME, #condition, __FILE__, __LINE__); \
+ return (val); \
+ } } while (0)
+
+#endif /* !DBUS_DISABLE_ASSERT */
+
+#define _DBUS_N_ELEMENTS(array) ((int) (sizeof ((array)) / sizeof ((array)[0])))
+
+#define _DBUS_POINTER_TO_INT(pointer) ((intptr_t)(pointer))
+#define _DBUS_INT_TO_POINTER(integer) ((void*)((intptr_t)(integer)))
+
+#define _DBUS_ZERO(object) (memset (&(object), '\0', sizeof ((object))))
+
+#ifdef offsetof
+#define _DBUS_STRUCT_OFFSET(struct_type, member) \
+ (offsetof (struct_type, member))
+#else
+#define _DBUS_STRUCT_OFFSET(struct_type, member) \
+ ((intptr_t) ((unsigned char*) &((struct_type*) 0)->member))
+#endif
+
+#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && !defined(__cplusplus)
+#define _DBUS_ALIGNOF(type) _Alignof(type)
+#else
+#define _DBUS_ALIGNOF(type) \
+ (_DBUS_STRUCT_OFFSET (struct { char _1; type _2; }, _2))
+#endif
+
+#if defined(DBUS_DISABLE_CHECKS) || defined(DBUS_DISABLE_ASSERT)
+/* this is an assert and not an error, but in the typical --disable-checks case (you're trying
+ * to really minimize code size), disabling these assertions makes sense.
+ */
+#define _DBUS_ASSERT_ERROR_IS_SET(error) do { } while (0)
+#define _DBUS_ASSERT_ERROR_IS_CLEAR(error) do { } while (0)
+#define _DBUS_ASSERT_ERROR_XOR_BOOL(error, retval) do { } while (0)
+#else
+static inline void
+_dbus_assert_error_is_set (const DBusError *error,
+ const char *file,
+ int line,
+ const char *func)
+{
+ _dbus_real_assert (error == NULL || dbus_error_is_set (error),
+ "error is set", file, line, func);
+}
+
+static inline void
+_dbus_assert_error_is_clear (const DBusError *error,
+ const char *file,
+ int line,
+ const char *func)
+{
+ _dbus_real_assert (error == NULL || !dbus_error_is_set (error),
+ "error is clear", file, line, func);
+}
+
+static inline void
+_dbus_assert_error_xor_bool (const DBusError *error,
+ dbus_bool_t retval,
+ const char *file,
+ int line,
+ const char *func)
+{
+ _dbus_real_assert (error == NULL || dbus_error_is_set (error) == !retval,
+ "error is consistent with boolean result", file, line, func);
+}
+
+/**
+ * Assert that error is set, unless it is NULL in which case we cannot
+ * tell whether it would have been set.
+ */
+#define _DBUS_ASSERT_ERROR_IS_SET(error) _dbus_assert_error_is_set (error, __FILE__, __LINE__, _DBUS_FUNCTION_NAME)
+
+/**
+ * Assert that error is not set, unless it is NULL in which case we cannot
+ * tell whether it would have been set.
+ */
+#define _DBUS_ASSERT_ERROR_IS_CLEAR(error) _dbus_assert_error_is_clear (error, __FILE__, __LINE__, _DBUS_FUNCTION_NAME)
+
+/**
+ * Assert that error is consistent with retval: if error is not NULL,
+ * it must be set if and only if retval is false.
+ *
+ * retval can be a boolean expression like "result != NULL".
+ */
+#define _DBUS_ASSERT_ERROR_XOR_BOOL(error, retval) _dbus_assert_error_xor_bool (error, retval, __FILE__, __LINE__, _DBUS_FUNCTION_NAME)
+#endif
+
+#define _dbus_return_if_error_is_set(error) _dbus_return_if_fail ((error) == NULL || !dbus_error_is_set ((error)))
+#define _dbus_return_val_if_error_is_set(error, val) _dbus_return_val_if_fail ((error) == NULL || !dbus_error_is_set ((error)), (val))
+
+/* This alignment thing is from ORBit2 */
+/* Align a value upward to a boundary, expressed as a number of bytes.
+ * E.g. align to an 8-byte boundary with argument of 8.
+ */
+
+/*
+ * (this + boundary - 1)
+ * &
+ * ~(boundary - 1)
+ */
+
+#define _DBUS_ALIGN_VALUE(this, boundary) \
+ ((((uintptr_t) (this)) + (((size_t) (boundary)) - 1)) & \
+ (~(((size_t) (boundary)) - 1)))
+
+#define _DBUS_ALIGN_ADDRESS(this, boundary) \
+ ((void*)_DBUS_ALIGN_VALUE(this, boundary))
+
+#define _DBUS_IS_ALIGNED(this, boundary) \
+ ((((size_t) (uintptr_t) (this)) & ((size_t) (boundary) - 1)) == 0)
+
+/**
+ * Aligning a pointer to _DBUS_ALIGNOF(dbus_max_align_t) guarantees that all
+ * scalar types can be loaded/stored from/to such an address without incurring
+ * an alignment fault (or a slow misaligned access).
+ * This is based on C11 max_align_t, but falls back to DBusBasicValue for
+ * older C standards.
+ */
+// #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)
+// typedef max_align_t dbus_max_align_t;
+// #else
+typedef DBusBasicValue dbus_max_align_t;
+// #endif
+
+DBUS_PRIVATE_EXPORT
+char* _dbus_strdup (const char *str);
+void* _dbus_memdup (const void *mem,
+ size_t n_bytes);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_string_array_contains (const char **array,
+ const char *str);
+DBUS_PRIVATE_EXPORT
+size_t _dbus_string_array_length (const char **array);
+char** _dbus_dup_string_array (const char **array);
+
+#define _DBUS_INT16_MIN ((dbus_int16_t) 0x8000)
+#define _DBUS_INT16_MAX ((dbus_int16_t) 0x7fff)
+#define _DBUS_UINT16_MAX ((dbus_uint16_t)0xffff)
+#define _DBUS_INT32_MIN ((dbus_int32_t) 0x80000000)
+#define _DBUS_INT32_MAX ((dbus_int32_t) 0x7fffffff)
+#define _DBUS_UINT32_MAX ((dbus_uint32_t)0xffffffff)
+/* using 32-bit here is sort of bogus */
+#define _DBUS_INT_MIN _DBUS_INT32_MIN
+#define _DBUS_INT_MAX _DBUS_INT32_MAX
+#define _DBUS_UINT_MAX _DBUS_UINT32_MAX
+#define _DBUS_INT64_MAX DBUS_INT64_CONSTANT (0x7fffffffffffffff)
+#define _DBUS_UINT64_MAX DBUS_UINT64_CONSTANT (0xffffffffffffffff)
+#define _DBUS_ONE_KILOBYTE 1024
+#define _DBUS_ONE_MEGABYTE 1024 * _DBUS_ONE_KILOBYTE
+#define _DBUS_ONE_HOUR_IN_MILLISECONDS (1000 * 60 * 60)
+#define _DBUS_USEC_PER_SECOND (1000000)
+
+#undef MAX
+#define MAX(a, b) (((a) > (b)) ? (a) : (b))
+
+#undef MIN
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+
+#undef ABS
+#define ABS(a) (((a) < 0) ? -(a) : (a))
+
+#define _DBUS_ISASCII(c) ((c) != '\0' && (((c) & ~0x7f) == 0))
+
+typedef void (* DBusForeachFunction) (void *element,
+ void *data);
+
+void _dbus_verbose_bytes (const unsigned char *data,
+ int len,
+ int offset);
+DBUS_PRIVATE_EXPORT
+void _dbus_verbose_bytes_of_string (const DBusString *str,
+ int start,
+ int len);
+
+DBUS_PRIVATE_EXPORT
+extern const char *_dbus_no_memory_message;
+#define _DBUS_SET_OOM(error) dbus_set_error_const ((error), DBUS_ERROR_NO_MEMORY, _dbus_no_memory_message)
+DBUS_PRIVATE_EXPORT
+void _dbus_set_error_valist (DBusError *error,
+ const char *name,
+ const char *format,
+ va_list args) _DBUS_GNUC_PRINTF (3, 0);
+
+typedef dbus_bool_t (* DBusTestMemoryFunction) (void *data,
+ dbus_bool_t have_memory);
+
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
+/* Memory debugging */
+void _dbus_set_fail_alloc_counter (int until_next_fail);
+int _dbus_get_fail_alloc_counter (void);
+void _dbus_set_fail_alloc_failures (int failures_per_failure);
+int _dbus_get_fail_alloc_failures (void);
+dbus_bool_t _dbus_decrement_fail_alloc_counter (void);
+dbus_bool_t _dbus_disable_mem_pools (void);
+DBUS_PRIVATE_EXPORT
+int _dbus_get_malloc_blocks_outstanding (void);
+
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_test_oom_handling (const char *description,
+ DBusTestMemoryFunction func,
+ void *data);
+#else
+#define _dbus_set_fail_alloc_counter(n)
+#define _dbus_get_fail_alloc_counter _DBUS_INT_MAX
+
+/* These are constant expressions so that blocks
+ * they protect should be optimized away
+ */
+#define _dbus_decrement_fail_alloc_counter() (FALSE)
+#define _dbus_disable_mem_pools() (FALSE)
+#define _dbus_get_malloc_blocks_outstanding() (0)
+
+#define _dbus_test_oom_handling(description, func, data) ((*func) (data, TRUE))
+#endif /* !DBUS_ENABLE_EMBEDDED_TESTS */
+
+typedef void (* DBusShutdownFunction) (void *data);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_register_shutdown_func (DBusShutdownFunction function,
+ void *data);
+dbus_bool_t _dbus_register_shutdown_func_unlocked (DBusShutdownFunction function,
+ void *data);
+
+extern int _dbus_current_generation;
+
+/* The weird case convention is to avoid having to change all the callers,
+ * which would be quite a mega-patch. */
+typedef enum
+{
+ /* index 0-4 */
+ _DBUS_LOCK_list,
+ _DBUS_LOCK_connection_slots,
+ _DBUS_LOCK_pending_call_slots,
+ _DBUS_LOCK_server_slots,
+ _DBUS_LOCK_message_slots,
+ /* index 5-9 */
+ _DBUS_LOCK_bus,
+ _DBUS_LOCK_bus_datas,
+ _DBUS_LOCK_shutdown_funcs,
+ _DBUS_LOCK_system_users,
+ _DBUS_LOCK_message_cache,
+ /* index 10-12 */
+ _DBUS_LOCK_shared_connections,
+ _DBUS_LOCK_machine_uuid,
+ _DBUS_LOCK_sysdeps,
+
+ _DBUS_N_GLOBAL_LOCKS
+} DBusGlobalLock;
+
+_DBUS_WARN_UNUSED_RESULT
+dbus_bool_t _dbus_lock (DBusGlobalLock lock);
+void _dbus_unlock (DBusGlobalLock lock);
+
+#define _DBUS_LOCK_NAME(name) _DBUS_LOCK_##name
+#define _DBUS_LOCK(name) _dbus_lock (_DBUS_LOCK_##name)
+#define _DBUS_UNLOCK(name) _dbus_unlock (_DBUS_LOCK_##name)
+
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_address_append_escaped (DBusString *escaped,
+ const DBusString *unescaped);
+
+void _dbus_set_bad_address (DBusError *error,
+ const char *address_problem_type,
+ const char *address_problem_field,
+ const char *address_problem_other);
+
+#define DBUS_UUID_LENGTH_BYTES 16
+#define DBUS_UUID_LENGTH_WORDS (DBUS_UUID_LENGTH_BYTES / 4)
+#define DBUS_UUID_LENGTH_HEX (DBUS_UUID_LENGTH_BYTES * 2)
+
+/**
+ * A globally unique ID ; we have one for each DBusServer, and also one for each
+ * machine with libdbus installed on it.
+ */
+union DBusGUID
+{
+ dbus_uint32_t as_uint32s[DBUS_UUID_LENGTH_WORDS]; /**< guid as four uint32 values */
+ char as_bytes[DBUS_UUID_LENGTH_BYTES]; /**< guid as 16 single-byte values */
+};
+
+DBUS_PRIVATE_EXPORT _DBUS_WARN_UNUSED_RESULT
+dbus_bool_t _dbus_generate_uuid (DBusGUID *uuid,
+ DBusError *error);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_uuid_encode (const DBusGUID *uuid,
+ DBusString *encoded);
+dbus_bool_t _dbus_read_uuid_file (const DBusString *filename,
+ DBusGUID *uuid,
+ dbus_bool_t create_if_not_found,
+ DBusError *error);
+
+dbus_bool_t _dbus_write_uuid_file (const DBusString *filename,
+ const DBusGUID *uuid,
+ DBusError *error);
+
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_get_local_machine_uuid_encoded (DBusString *uuid_str,
+ DBusError *error);
+
+#define _DBUS_PASTE2(a, b) a ## b
+#define _DBUS_PASTE(a, b) _DBUS_PASTE2 (a, b)
+#define _DBUS_STATIC_ASSERT(expr) \
+ typedef struct { char _assertion[(expr) ? 1 : -1]; } \
+ _DBUS_PASTE (_DBUS_STATIC_ASSERT_, __LINE__) _DBUS_GNUC_UNUSED
+
+#define _DBUS_STRINGIFY(x) #x
+#define _DBUS_FILE_LINE __FILE__ ":" _DBUS_STRINGIFY(__LINE__)
+
+#ifndef __has_feature
+# define __has_feature(x) 0
+#endif
+
+/* MSVC defines __SANITIZE_ADDRESS__, but does not provide the special
+ * builtins associated with it. */
+#if ((defined(__SANITIZE_ADDRESS__) || __has_feature(address_sanitizer)) && \
+ !defined(_MSC_VER))
+# include <sanitizer/lsan_interface.h>
+/* Defined if we are building with AddressSanitizer */
+# define _DBUS_ADDRESS_SANITIZER
+/* Ignore memory allocations until the next _DBUS_END_IGNORE_LEAKS when
+ * checking for memory leaks */
+# define _DBUS_BEGIN_IGNORE_LEAKS __lsan_disable ()
+/* End the scope of a previous _DBUS_BEGIN_IGNORE_LEAKS */
+# define _DBUS_END_IGNORE_LEAKS __lsan_enable ()
+#else
+# undef _DBUS_ADDRESS_SANITIZER
+# define _DBUS_BEGIN_IGNORE_LEAKS do { } while (0)
+# define _DBUS_END_IGNORE_LEAKS do { } while (0)
+#endif
+
+/** @def DBUS_IS_DIR_SEPARATOR(c)
+ * macro for checking if character c is a path separator
+ */
+#ifdef DBUS_WIN
+#define DBUS_IS_DIR_SEPARATOR(c) (c == '\\' || c == '/')
+#define DBUS_DIR_SEPARATOR '\\'
+#define DBUS_DIR_SEPARATOR_S "\\"
+#else
+#define DBUS_IS_DIR_SEPARATOR(c) (c == '/')
+#define DBUS_DIR_SEPARATOR '/'
+#define DBUS_DIR_SEPARATOR_S "/"
+#endif
+
+DBUS_END_DECLS
+
+#endif /* DBUS_INTERNALS_H */
diff --git a/src/3rdparty/libdbus/dbus/dbus-keyring.c b/src/3rdparty/libdbus/dbus/dbus-keyring.c
new file mode 100644
index 00000000..f55b21a8
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-keyring.c
@@ -0,0 +1,1155 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-keyring.c Store secret cookies in your homedir
+ *
+ * Copyright (C) 2003, 2004 Red Hat Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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-keyring.h"
+#include "dbus-protocol.h"
+#include <dbus/dbus-string.h>
+#include <dbus/dbus-list.h>
+#include <dbus/dbus-sysdeps.h>
+#include <dbus/dbus-test-tap.h>
+
+/**
+ * @defgroup DBusKeyring keyring class
+ * @ingroup DBusInternals
+ * @brief DBusKeyring data structure
+ *
+ * Types and functions related to DBusKeyring. DBusKeyring is intended
+ * to manage cookies used to authenticate clients to servers. This is
+ * essentially the "verify that client can read the user's homedir"
+ * authentication mechanism. Both client and server must have access
+ * to the homedir.
+ *
+ * The secret keys are not kept in locked memory, and are written to a
+ * file in the user's homedir. However they are transient (only used
+ * by a single server instance for a fixed period of time, then
+ * discarded). Also, the keys are not sent over the wire.
+ *
+ * @todo there's a memory leak on some codepath in here, I saw it once
+ * when running make check - probably some specific initial cookies
+ * present in the cookie file, then depending on what we do with them.
+ */
+
+/**
+ * @defgroup DBusKeyringInternals DBusKeyring implementation details
+ * @ingroup DBusInternals
+ * @brief DBusKeyring implementation details
+ *
+ * The guts of DBusKeyring.
+ *
+ * @{
+ */
+
+/** The maximum age of a key before we create a new key to use in
+ * challenges. This isn't super-reliably enforced, since system
+ * clocks can change or be wrong, but we make a best effort to only
+ * use keys for a short time.
+ */
+#define NEW_KEY_TIMEOUT_SECONDS (60*5)
+/**
+ * The time after which we drop a key from the secrets file.
+ * The EXPIRE_KEYS_TIMEOUT_SECONDS - NEW_KEY_TIMEOUT_SECONDS is the minimum
+ * time window a client has to complete authentication.
+ */
+#define EXPIRE_KEYS_TIMEOUT_SECONDS (NEW_KEY_TIMEOUT_SECONDS + (60*2))
+/**
+ * The maximum amount of time a key can be in the future.
+ */
+#define MAX_TIME_TRAVEL_SECONDS (60*5)
+
+/**
+ * Maximum number of keys in the keyring before
+ * we just ignore the rest
+ */
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
+#define MAX_KEYS_IN_FILE 10
+#else
+#define MAX_KEYS_IN_FILE 256
+#endif
+
+/**
+ * A single key from the cookie file
+ */
+typedef struct
+{
+ dbus_int32_t id; /**< identifier used to refer to the key */
+
+ dbus_int64_t creation_time; /**< when the key was generated, in seconds since 1970-01-01 */
+
+ DBusString secret; /**< the actual key */
+
+} DBusKey;
+
+/**
+ * @brief Internals of DBusKeyring.
+ *
+ * DBusKeyring internals. DBusKeyring is an opaque object, it must be
+ * used via accessor functions.
+ */
+struct DBusKeyring
+{
+ int refcount; /**< Reference count */
+ DBusString directory; /**< Directory the below two items are inside */
+ DBusString filename; /**< Keyring filename */
+ DBusString filename_lock; /**< Name of lockfile */
+ DBusKey *keys; /**< Keys loaded from the file */
+ int n_keys; /**< Number of keys */
+ DBusCredentials *credentials; /**< Credentials containing user the keyring is for */
+};
+
+static DBusKeyring*
+_dbus_keyring_new (void)
+{
+ DBusKeyring *keyring;
+
+ keyring = dbus_new0 (DBusKeyring, 1);
+ if (keyring == NULL)
+ goto out_0;
+
+ if (!_dbus_string_init (&keyring->directory))
+ goto out_1;
+
+ if (!_dbus_string_init (&keyring->filename))
+ goto out_2;
+
+ if (!_dbus_string_init (&keyring->filename_lock))
+ goto out_3;
+
+ keyring->refcount = 1;
+ keyring->keys = NULL;
+ keyring->n_keys = 0;
+
+ return keyring;
+
+ out_3:
+ _dbus_string_free (&keyring->filename);
+ out_2:
+ _dbus_string_free (&keyring->directory);
+ out_1:
+ dbus_free (keyring);
+ out_0:
+ return NULL;
+}
+
+static void
+free_keys (DBusKey *keys,
+ int n_keys)
+{
+ int i;
+
+ /* should be safe for args NULL, 0 */
+
+ i = 0;
+ while (i < n_keys)
+ {
+ _dbus_string_free (&keys[i].secret);
+ ++i;
+ }
+
+ dbus_free (keys);
+}
+
+/* Our locking scheme is highly unreliable. However, there is
+ * unfortunately no reliable locking scheme in user home directories;
+ * between bugs in Linux NFS, people using Tru64 or other total crap
+ * NFS, AFS, random-file-system-of-the-week, and so forth, fcntl() in
+ * homedirs simply generates tons of bug reports. This has been
+ * learned through hard experience with GConf, unfortunately.
+ *
+ * This bad hack might work better for the kind of lock we have here,
+ * which we don't expect to hold for any length of time. Crashing
+ * while we hold it should be unlikely, and timing out such that we
+ * delete a stale lock should also be unlikely except when the
+ * filesystem is running really slowly. Stuff might break in corner
+ * cases but as long as it's not a security-level breakage it should
+ * be OK.
+ */
+
+/** Maximum number of timeouts waiting for lock before we decide it's stale */
+#define MAX_LOCK_TIMEOUTS 32
+/** Length of each timeout while waiting for a lock */
+#define LOCK_TIMEOUT_MILLISECONDS 250
+
+static dbus_bool_t
+_dbus_keyring_lock (DBusKeyring *keyring)
+{
+ int n_timeouts;
+
+ n_timeouts = 0;
+ while (n_timeouts < MAX_LOCK_TIMEOUTS)
+ {
+ DBusError error = DBUS_ERROR_INIT;
+
+ if (_dbus_create_file_exclusively (&keyring->filename_lock,
+ &error))
+ break;
+
+ _dbus_verbose ("Did not get lock file, sleeping %d milliseconds (%s)\n",
+ LOCK_TIMEOUT_MILLISECONDS, error.message);
+ dbus_error_free (&error);
+
+ _dbus_sleep_milliseconds (LOCK_TIMEOUT_MILLISECONDS);
+
+ ++n_timeouts;
+ }
+
+ if (n_timeouts == MAX_LOCK_TIMEOUTS)
+ {
+ DBusError error = DBUS_ERROR_INIT;
+
+ _dbus_verbose ("Lock file timed out %d times, assuming stale\n",
+ n_timeouts);
+
+ if (!_dbus_delete_file (&keyring->filename_lock, &error))
+ {
+ _dbus_verbose ("Couldn't delete old lock file: %s\n",
+ error.message);
+ dbus_error_free (&error);
+ return FALSE;
+ }
+
+ if (!_dbus_create_file_exclusively (&keyring->filename_lock,
+ &error))
+ {
+ _dbus_verbose ("Couldn't create lock file after deleting stale one: %s\n",
+ error.message);
+ dbus_error_free (&error);
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+static void
+_dbus_keyring_unlock (DBusKeyring *keyring)
+{
+ DBusError error = DBUS_ERROR_INIT;
+
+ if (!_dbus_delete_file (&keyring->filename_lock, &error))
+ {
+ _dbus_warn ("Failed to delete lock file: %s",
+ error.message);
+ dbus_error_free (&error);
+ }
+}
+
+static DBusKey*
+find_key_by_id (DBusKey *keys,
+ int n_keys,
+ int id)
+{
+ int i;
+
+ i = 0;
+ while (i < n_keys)
+ {
+ if (keys[i].id == id)
+ return &keys[i];
+
+ ++i;
+ }
+
+ return NULL;
+}
+
+static dbus_bool_t
+add_new_key (DBusKey **keys_p,
+ int *n_keys_p,
+ DBusError *error)
+{
+ DBusKey *new;
+ DBusString bytes;
+ int id;
+ dbus_int64_t timestamp;
+ const unsigned char *s;
+ dbus_bool_t retval;
+ DBusKey *keys;
+ int n_keys;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ if (!_dbus_string_init (&bytes))
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ return FALSE;
+ }
+
+ keys = *keys_p;
+ n_keys = *n_keys_p;
+ retval = FALSE;
+
+ /* Generate an integer ID and then the actual key. */
+ retry:
+
+ if (!_dbus_generate_random_bytes (&bytes, 4, error))
+ goto out;
+
+ s = (const unsigned char*) _dbus_string_get_const_data (&bytes);
+
+ id = s[0] | (s[1] << 8) | (s[2] << 16) | ((s[3] & 0x7f) << 24);
+ _dbus_assert (id >= 0);
+
+ if (find_key_by_id (keys, n_keys, id) != NULL)
+ {
+ _dbus_string_set_length (&bytes, 0);
+ _dbus_verbose ("Key ID %d already existed, trying another one\n",
+ id);
+ goto retry;
+ }
+
+ _dbus_verbose ("Creating key with ID %d\n", id);
+
+#define KEY_LENGTH_BYTES 24
+ _dbus_string_set_length (&bytes, 0);
+ if (!_dbus_generate_random_bytes (&bytes, KEY_LENGTH_BYTES, error))
+ {
+ goto out;
+ }
+
+ new = dbus_realloc (keys, sizeof (DBusKey) * (n_keys + 1));
+ if (new == NULL)
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ goto out;
+ }
+
+ keys = new;
+ *keys_p = keys; /* otherwise *keys_p ends up invalid */
+ n_keys += 1;
+
+ if (!_dbus_string_init (&keys[n_keys-1].secret))
+ {
+ n_keys -= 1; /* we don't want to free the one we didn't init */
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ goto out;
+ }
+
+ _dbus_get_real_time (&timestamp, NULL);
+
+ keys[n_keys-1].id = id;
+ keys[n_keys-1].creation_time = timestamp;
+ if (!_dbus_string_move (&bytes, 0,
+ &keys[n_keys-1].secret,
+ 0))
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ _dbus_string_free (&keys[n_keys-1].secret);
+ n_keys -= 1;
+ goto out;
+ }
+
+ retval = TRUE;
+
+ out:
+ *n_keys_p = n_keys;
+
+ _dbus_string_free (&bytes);
+ return retval;
+}
+
+/**
+ * Reloads the keyring file, optionally adds one new key to the file,
+ * removes all expired keys from the file iff a key was added, then
+ * resaves the file. Stores the keys from the file in keyring->keys.
+ * Note that the file is only resaved (written to) if a key is added,
+ * this means that only servers ever write to the file and need to
+ * lock it, which avoids a lot of lock contention at login time and
+ * such.
+ *
+ * @param keyring the keyring
+ * @param add_new #TRUE to add a new key to the file, expire keys, and resave
+ * @param error return location for errors
+ * @returns #FALSE on failure
+ */
+static dbus_bool_t
+_dbus_keyring_reload (DBusKeyring *keyring,
+ dbus_bool_t add_new,
+ DBusError *error)
+{
+ DBusString contents;
+ DBusString line;
+ dbus_bool_t retval;
+ dbus_bool_t have_lock;
+ DBusKey *keys;
+ int n_keys;
+ int i;
+ dbus_int64_t now;
+ DBusError tmp_error;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ if (!_dbus_check_dir_is_private_to_user (&keyring->directory, error))
+ return FALSE;
+
+ if (!_dbus_string_init (&contents))
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ return FALSE;
+ }
+
+ if (!_dbus_string_init (&line))
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ _dbus_string_free (&contents);
+ return FALSE;
+ }
+
+ keys = NULL;
+ n_keys = 0;
+ retval = FALSE;
+ have_lock = FALSE;
+
+ _dbus_get_real_time (&now, NULL);
+
+ if (add_new)
+ {
+ if (!_dbus_keyring_lock (keyring))
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "Could not lock keyring file to add to it");
+ goto out;
+ }
+
+ have_lock = TRUE;
+ }
+
+ dbus_error_init (&tmp_error);
+ if (!_dbus_file_get_contents (&contents,
+ &keyring->filename,
+ &tmp_error))
+ {
+ _dbus_verbose ("Failed to load keyring file: %s\n",
+ tmp_error.message);
+ /* continue with empty keyring file, so we recreate it */
+ dbus_error_free (&tmp_error);
+ }
+
+ if (!_dbus_string_validate_ascii (&contents, 0,
+ _dbus_string_get_length (&contents)))
+ {
+ _dbus_warn ("Secret keyring file contains non-ASCII! Ignoring existing contents");
+ _dbus_string_set_length (&contents, 0);
+ }
+
+ /* FIXME this is badly inefficient for large keyring files
+ * (not that large keyring files exist outside of test suites)
+ */
+ while (_dbus_string_pop_line (&contents, &line))
+ {
+ int next;
+ long val;
+ int id;
+ dbus_int64_t timestamp;
+ int len;
+ int end;
+ DBusKey *new;
+
+ /* Don't load more than the max. */
+ if (n_keys >= (add_new ? MAX_KEYS_IN_FILE - 1 : MAX_KEYS_IN_FILE))
+ break;
+
+ next = 0;
+ if (!_dbus_string_parse_int (&line, 0, &val, &next))
+ {
+ _dbus_verbose ("could not parse secret key ID at start of line\n");
+ continue;
+ }
+
+ if (val > _DBUS_INT32_MAX || val < 0)
+ {
+ _dbus_verbose ("invalid secret key ID at start of line\n");
+ continue;
+ }
+
+ id = val;
+
+ _dbus_string_skip_blank (&line, next, &next);
+
+ if (!_dbus_string_parse_int64 (&line, next, &timestamp, &next))
+ {
+ _dbus_verbose ("could not parse secret key timestamp\n");
+ continue;
+ }
+
+ if (timestamp < 0 ||
+ (now + MAX_TIME_TRAVEL_SECONDS) < timestamp ||
+ (now - EXPIRE_KEYS_TIMEOUT_SECONDS) > timestamp)
+ {
+ _dbus_verbose ("dropping/ignoring %" DBUS_INT64_MODIFIER "d-seconds old key with timestamp %" DBUS_INT64_MODIFIER "d as current time is %" DBUS_INT64_MODIFIER "d\n",
+ now - timestamp, timestamp, now);
+ continue;
+ }
+
+ _dbus_string_skip_blank (&line, next, &next);
+
+ len = _dbus_string_get_length (&line);
+
+ if ((len - next) == 0)
+ {
+ _dbus_verbose ("no secret key after ID and timestamp\n");
+ continue;
+ }
+
+ /* We have all three parts */
+ new = dbus_realloc (keys, sizeof (DBusKey) * (n_keys + 1));
+ if (new == NULL)
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ goto out;
+ }
+
+ keys = new;
+ n_keys += 1;
+
+ if (!_dbus_string_init (&keys[n_keys-1].secret))
+ {
+ n_keys -= 1; /* we don't want to free the one we didn't init */
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ goto out;
+ }
+
+ keys[n_keys-1].id = id;
+ keys[n_keys-1].creation_time = timestamp;
+ if (!_dbus_string_hex_decode (&line, next, &end,
+ &keys[n_keys-1].secret, 0))
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ goto out;
+ }
+
+ if (_dbus_string_get_length (&line) != end)
+ {
+ _dbus_verbose ("invalid hex encoding in keyring file\n");
+ _dbus_string_free (&keys[n_keys - 1].secret);
+ n_keys -= 1;
+ continue;
+ }
+ }
+
+ _dbus_verbose ("Successfully loaded %d existing keys\n",
+ n_keys);
+
+ if (add_new)
+ {
+ if (!add_new_key (&keys, &n_keys, error))
+ {
+ _dbus_verbose ("Failed to generate new key: %s\n",
+ error ? error->message : "(unknown)");
+ goto out;
+ }
+
+ _dbus_string_set_length (&contents, 0);
+
+ i = 0;
+ while (i < n_keys)
+ {
+ if (!_dbus_string_append_int (&contents,
+ keys[i].id))
+ goto nomem;
+
+ if (!_dbus_string_append_byte (&contents, ' '))
+ goto nomem;
+
+ if (!_dbus_string_append_printf (&contents, "%" DBUS_INT64_MODIFIER "d",
+ keys[i].creation_time))
+ goto nomem;
+
+ if (!_dbus_string_append_byte (&contents, ' '))
+ goto nomem;
+
+ if (!_dbus_string_hex_encode (&keys[i].secret, 0,
+ &contents,
+ _dbus_string_get_length (&contents)))
+ goto nomem;
+
+ if (!_dbus_string_append_byte (&contents, '\n'))
+ goto nomem;
+
+ ++i;
+ continue;
+
+ nomem:
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ goto out;
+ }
+
+ if (!_dbus_string_save_to_file (&contents, &keyring->filename,
+ FALSE, error))
+ goto out;
+ }
+
+ if (keyring->keys)
+ free_keys (keyring->keys, keyring->n_keys);
+ keyring->keys = keys;
+ keyring->n_keys = n_keys;
+ keys = NULL;
+ n_keys = 0;
+
+ retval = TRUE;
+
+ out:
+ if (have_lock)
+ _dbus_keyring_unlock (keyring);
+
+ if (! ((retval == TRUE && (error == NULL || error->name == NULL)) ||
+ (retval == FALSE && (error == NULL || error->name != NULL))))
+ {
+ if (error && error->name)
+ _dbus_verbose ("error is %s: %s\n", error->name, error->message);
+ _dbus_warn ("returning %d but error pointer %p name %s",
+ retval, error, error->name ? error->name : "(none)");
+ _dbus_assert_not_reached ("didn't handle errors properly");
+ }
+
+ if (keys != NULL)
+ {
+ i = 0;
+ while (i < n_keys)
+ {
+ _dbus_string_zero (&keys[i].secret);
+ _dbus_string_free (&keys[i].secret);
+ ++i;
+ }
+
+ dbus_free (keys);
+ }
+
+ _dbus_string_free (&contents);
+ _dbus_string_free (&line);
+
+ return retval;
+}
+
+/** @} */ /* end of internals */
+
+/**
+ * @addtogroup DBusKeyring
+ *
+ * @{
+ */
+
+/**
+ * Increments reference count of the keyring
+ *
+ * @param keyring the keyring
+ * @returns the keyring
+ */
+DBusKeyring *
+_dbus_keyring_ref (DBusKeyring *keyring)
+{
+ keyring->refcount += 1;
+
+ return keyring;
+}
+
+/**
+ * Decrements refcount and finalizes if it reaches
+ * zero.
+ *
+ * @param keyring the keyring
+ */
+void
+_dbus_keyring_unref (DBusKeyring *keyring)
+{
+ keyring->refcount -= 1;
+
+ if (keyring->refcount == 0)
+ {
+ if (keyring->credentials)
+ _dbus_credentials_unref (keyring->credentials);
+
+ _dbus_string_free (&keyring->filename);
+ _dbus_string_free (&keyring->filename_lock);
+ _dbus_string_free (&keyring->directory);
+ free_keys (keyring->keys, keyring->n_keys);
+ dbus_free (keyring);
+ }
+}
+
+/**
+ * Creates a new keyring that lives in the ~/.dbus-keyrings directory
+ * of the user represented by @p credentials. If the @p credentials are
+ * #NULL or empty, uses those of the current process.
+ *
+ * @param credentials a set of credentials representing a user or #NULL
+ * @param context which keyring to get
+ * @param error return location for errors
+ * @returns the keyring or #NULL on error
+ */
+DBusKeyring*
+_dbus_keyring_new_for_credentials (DBusCredentials *credentials,
+ const DBusString *context,
+ DBusError *error)
+{
+ DBusString ringdir;
+ DBusKeyring *keyring;
+ dbus_bool_t error_set;
+ DBusError tmp_error;
+ DBusCredentials *our_credentials;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ if (_dbus_check_setuid ())
+ {
+ dbus_set_error_const (error, DBUS_ERROR_NOT_SUPPORTED,
+ "Unable to create DBus keyring when setuid");
+ return NULL;
+ }
+
+ keyring = NULL;
+ error_set = FALSE;
+ our_credentials = NULL;
+
+ if (!_dbus_string_init (&ringdir))
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ return NULL;
+ }
+
+ if (credentials != NULL)
+ {
+ our_credentials = _dbus_credentials_copy (credentials);
+ }
+ else
+ {
+ our_credentials = _dbus_credentials_new_from_current_process ();
+ }
+
+ if (our_credentials == NULL)
+ goto failed;
+
+ if (_dbus_credentials_are_anonymous (our_credentials))
+ {
+ if (!_dbus_credentials_add_from_current_process (our_credentials))
+ goto failed;
+ }
+
+ if (!_dbus_append_keyring_directory_for_credentials (&ringdir,
+ our_credentials))
+ goto failed;
+
+ keyring = _dbus_keyring_new ();
+ if (keyring == NULL)
+ goto failed;
+
+ _dbus_assert (keyring->credentials == NULL);
+ keyring->credentials = our_credentials;
+ our_credentials = NULL; /* so we don't unref it again later */
+
+ /* should have been validated already, but paranoia check here */
+ if (!_dbus_keyring_validate_context (context))
+ {
+ error_set = TRUE;
+ dbus_set_error_const (error,
+ DBUS_ERROR_FAILED,
+ "Invalid context in keyring creation");
+ goto failed;
+ }
+
+ /* Save keyring dir in the keyring object */
+ if (!_dbus_string_copy (&ringdir, 0,
+ &keyring->directory, 0))
+ goto failed;
+
+ /* Create keyring->filename based on keyring dir and context */
+ if (!_dbus_string_copy (&keyring->directory, 0,
+ &keyring->filename, 0))
+ goto failed;
+
+ if (!_dbus_concat_dir_and_file (&keyring->filename,
+ context))
+ goto failed;
+
+ /* Create lockfile name */
+ if (!_dbus_string_copy (&keyring->filename, 0,
+ &keyring->filename_lock, 0))
+ goto failed;
+
+ if (!_dbus_string_append (&keyring->filename_lock, ".lock"))
+ goto failed;
+
+ /* Reload keyring */
+ dbus_error_init (&tmp_error);
+ if (!_dbus_keyring_reload (keyring, FALSE, &tmp_error))
+ {
+ _dbus_verbose ("didn't load an existing keyring: %s\n",
+ tmp_error.message);
+ dbus_error_free (&tmp_error);
+ }
+
+ /* We don't fail fatally if we can't create the directory,
+ * but the keyring will probably always be empty
+ * unless someone else manages to create it
+ */
+ dbus_error_init (&tmp_error);
+ if (!_dbus_ensure_directory (&keyring->directory,
+ &tmp_error))
+ {
+ _dbus_verbose ("Creating keyring directory: %s\n",
+ tmp_error.message);
+ dbus_error_free (&tmp_error);
+ }
+
+ _dbus_string_free (&ringdir);
+
+ return keyring;
+
+ failed:
+ if (!error_set)
+ dbus_set_error_const (error,
+ DBUS_ERROR_NO_MEMORY,
+ NULL);
+ if (our_credentials)
+ _dbus_credentials_unref (our_credentials);
+ if (keyring)
+ _dbus_keyring_unref (keyring);
+ _dbus_string_free (&ringdir);
+ return NULL;
+
+}
+
+/**
+ * Checks whether the context is a valid context.
+ * Contexts that might cause confusion when used
+ * in filenames are not allowed (contexts can't
+ * start with a dot or contain dir separators).
+ *
+ * @todo this is the most inefficient implementation
+ * imaginable.
+ *
+ * @param context the context
+ * @returns #TRUE if valid
+ */
+dbus_bool_t
+_dbus_keyring_validate_context (const DBusString *context)
+{
+ if (_dbus_string_get_length (context) == 0)
+ {
+ _dbus_verbose ("context is zero-length\n");
+ return FALSE;
+ }
+
+ if (!_dbus_string_validate_ascii (context, 0,
+ _dbus_string_get_length (context)))
+ {
+ _dbus_verbose ("context not valid ascii\n");
+ return FALSE;
+ }
+
+ /* no directory separators */
+ if (_dbus_string_find (context, 0, "/", NULL))
+ {
+ _dbus_verbose ("context contains a slash\n");
+ return FALSE;
+ }
+
+ if (_dbus_string_find (context, 0, "\\", NULL))
+ {
+ _dbus_verbose ("context contains a backslash\n");
+ return FALSE;
+ }
+
+ /* prevent attempts to use dotfiles or ".." or ".lock"
+ * all of which might allow some kind of attack
+ */
+ if (_dbus_string_find (context, 0, ".", NULL))
+ {
+ _dbus_verbose ("context contains a dot\n");
+ return FALSE;
+ }
+
+ /* no spaces/tabs, those are used for separators in the protocol */
+ if (_dbus_string_find_blank (context, 0, NULL))
+ {
+ _dbus_verbose ("context contains a blank\n");
+ return FALSE;
+ }
+
+ if (_dbus_string_find (context, 0, "\n", NULL))
+ {
+ _dbus_verbose ("context contains a newline\n");
+ return FALSE;
+ }
+
+ if (_dbus_string_find (context, 0, "\r", NULL))
+ {
+ _dbus_verbose ("context contains a carriage return\n");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static DBusKey*
+find_recent_key (DBusKeyring *keyring)
+{
+ int i;
+ dbus_int64_t tv_sec;
+ long tv_usec;
+
+ _dbus_get_real_time (&tv_sec, &tv_usec);
+
+ i = 0;
+ while (i < keyring->n_keys)
+ {
+ DBusKey *key = &keyring->keys[i];
+
+ _dbus_verbose ("Key %d is %" DBUS_INT64_MODIFIER "d seconds old\n",
+ i, tv_sec - key->creation_time);
+
+ if ((tv_sec - NEW_KEY_TIMEOUT_SECONDS) < key->creation_time)
+ return key;
+
+ ++i;
+ }
+
+ return NULL;
+}
+
+/**
+ * Gets a recent key to use for authentication.
+ * If no recent key exists, creates one. Returns
+ * the key ID. If a key can't be written to the keyring
+ * file so no recent key can be created, returns -1.
+ * All valid keys are > 0.
+ *
+ * @param keyring the keyring
+ * @param error error on failure
+ * @returns key ID to use for auth, or -1 on failure
+ */
+int
+_dbus_keyring_get_best_key (DBusKeyring *keyring,
+ DBusError *error)
+{
+ DBusKey *key;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ key = find_recent_key (keyring);
+ if (key)
+ return key->id;
+
+ /* All our keys are too old, or we've never loaded the
+ * keyring. Create a new one.
+ */
+ if (!_dbus_keyring_reload (keyring, TRUE,
+ error))
+ return -1;
+
+ key = find_recent_key (keyring);
+ if (key)
+ return key->id;
+ else
+ {
+ dbus_set_error_const (error,
+ DBUS_ERROR_FAILED,
+ "No recent-enough key found in keyring, and unable to create a new key");
+ return -1;
+ }
+}
+
+/**
+ * Checks whether the keyring is for the same user as the given credentials.
+ *
+ * @param keyring the keyring
+ * @param credentials the credentials to check
+ *
+ * @returns #TRUE if the keyring belongs to the given user
+ */
+dbus_bool_t
+_dbus_keyring_is_for_credentials (DBusKeyring *keyring,
+ DBusCredentials *credentials)
+{
+ return _dbus_credentials_same_user (keyring->credentials,
+ credentials);
+}
+
+/**
+ * Gets the hex-encoded secret key for the given ID.
+ * Returns #FALSE if not enough memory. Returns #TRUE
+ * but empty key on any other error such as unknown
+ * key ID.
+ *
+ * @param keyring the keyring
+ * @param key_id the key ID
+ * @param hex_key string to append hex-encoded key to
+ * @returns #TRUE if we had enough memory
+ */
+dbus_bool_t
+_dbus_keyring_get_hex_key (DBusKeyring *keyring,
+ int key_id,
+ DBusString *hex_key)
+{
+ DBusKey *key;
+
+ key = find_key_by_id (keyring->keys,
+ keyring->n_keys,
+ key_id);
+ if (key == NULL)
+ return TRUE; /* had enough memory, so TRUE */
+
+ return _dbus_string_hex_encode (&key->secret, 0,
+ hex_key,
+ _dbus_string_get_length (hex_key));
+}
+
+/** @} */ /* end of exposed API */
+
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
+#include "dbus-test.h"
+#include <stdio.h>
+
+dbus_bool_t
+_dbus_keyring_test (const char *test_data_dir _DBUS_GNUC_UNUSED)
+{
+ DBusString context;
+ DBusKeyring *ring1;
+ DBusKeyring *ring2;
+ int id;
+ DBusError error;
+ int i;
+
+ ring1 = NULL;
+ ring2 = NULL;
+
+ /* Context validation */
+
+ _dbus_string_init_const (&context, "foo");
+ _dbus_assert (_dbus_keyring_validate_context (&context));
+ _dbus_string_init_const (&context, "org_freedesktop_blah");
+ _dbus_assert (_dbus_keyring_validate_context (&context));
+
+ _dbus_string_init_const (&context, "");
+ _dbus_assert (!_dbus_keyring_validate_context (&context));
+ _dbus_string_init_const (&context, ".foo");
+ _dbus_assert (!_dbus_keyring_validate_context (&context));
+ _dbus_string_init_const (&context, "bar.foo");
+ _dbus_assert (!_dbus_keyring_validate_context (&context));
+ _dbus_string_init_const (&context, "bar/foo");
+ _dbus_assert (!_dbus_keyring_validate_context (&context));
+ _dbus_string_init_const (&context, "bar\\foo");
+ _dbus_assert (!_dbus_keyring_validate_context (&context));
+ _dbus_string_init_const (&context, "foo\xfa\xf0");
+ _dbus_assert (!_dbus_keyring_validate_context (&context));
+ _dbus_string_init_const (&context, "foo\x80");
+ _dbus_assert (!_dbus_keyring_validate_context (&context));
+ _dbus_string_init_const (&context, "foo\x7f");
+ _dbus_assert (_dbus_keyring_validate_context (&context));
+ _dbus_string_init_const (&context, "foo bar");
+ _dbus_assert (!_dbus_keyring_validate_context (&context));
+
+ if (!_dbus_string_init (&context))
+ _dbus_test_fatal ("no memory");
+ if (!_dbus_string_append_byte (&context, '\0'))
+ _dbus_test_fatal ("no memory");
+ _dbus_assert (!_dbus_keyring_validate_context (&context));
+ _dbus_string_free (&context);
+
+ /* Now verify that if we create a key in keyring 1,
+ * it is properly loaded in keyring 2
+ */
+
+ _dbus_string_init_const (&context, "org_freedesktop_dbus_testsuite");
+ dbus_error_init (&error);
+ ring1 = _dbus_keyring_new_for_credentials (NULL, &context,
+ &error);
+ _dbus_assert (ring1 != NULL);
+ _dbus_assert (error.name == NULL);
+
+ id = _dbus_keyring_get_best_key (ring1, &error);
+ if (id < 0)
+ {
+ fprintf (stderr, "Could not load keyring: %s\n", error.message);
+ dbus_error_free (&error);
+ goto failure;
+ }
+
+ ring2 = _dbus_keyring_new_for_credentials (NULL, &context, &error);
+ _dbus_assert (ring2 != NULL);
+ _dbus_assert (error.name == NULL);
+
+ if (ring1->n_keys != ring2->n_keys)
+ {
+ fprintf (stderr, "Different number of keys in keyrings\n");
+ goto failure;
+ }
+
+ /* We guarantee we load and save keeping keys in a fixed
+ * order
+ */
+ i = 0;
+ while (i < ring1->n_keys)
+ {
+ if (ring1->keys[i].id != ring2->keys[i].id)
+ {
+ fprintf (stderr, "Keyring 1 has first key ID %d and keyring 2 has %d\n",
+ ring1->keys[i].id, ring2->keys[i].id);
+ goto failure;
+ }
+
+ if (ring1->keys[i].creation_time != ring2->keys[i].creation_time)
+ {
+ fprintf (stderr, "Keyring 1 has first key time %" DBUS_INT64_MODIFIER "d and keyring 2 has %" DBUS_INT64_MODIFIER "d\n",
+ ring1->keys[i].creation_time, ring2->keys[i].creation_time);
+ goto failure;
+ }
+
+ if (!_dbus_string_equal (&ring1->keys[i].secret,
+ &ring2->keys[i].secret))
+ {
+ fprintf (stderr, "Keyrings 1 and 2 have different secrets for same ID/timestamp\n");
+ goto failure;
+ }
+
+ ++i;
+ }
+
+ _dbus_test_diag (" %d keys in test", ring1->n_keys);
+
+ /* Test ref/unref */
+ _dbus_keyring_ref (ring1);
+ _dbus_keyring_ref (ring2);
+ _dbus_keyring_unref (ring1);
+ _dbus_keyring_unref (ring2);
+
+
+ /* really unref */
+ _dbus_keyring_unref (ring1);
+ _dbus_keyring_unref (ring2);
+
+ return TRUE;
+
+ failure:
+ if (ring1)
+ _dbus_keyring_unref (ring1);
+ if (ring2)
+ _dbus_keyring_unref (ring2);
+
+ return FALSE;
+}
+
+#endif /* DBUS_ENABLE_EMBEDDED_TESTS */
+
diff --git a/src/3rdparty/libdbus/dbus/dbus-keyring.h b/src/3rdparty/libdbus/dbus/dbus-keyring.h
new file mode 100644
index 00000000..fe122b8e
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-keyring.h
@@ -0,0 +1,54 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-keyring.h Store secret cookies in your homedir
+ *
+ * Copyright (C) 2003 Red Hat Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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
+ *
+ */
+#ifndef DBUS_KEYRING_H
+#define DBUS_KEYRING_H
+
+#include <dbus/dbus-macros.h>
+#include <dbus/dbus-errors.h>
+#include <dbus/dbus-string.h>
+#include <dbus/dbus-credentials.h>
+
+DBUS_BEGIN_DECLS
+
+typedef struct DBusKeyring DBusKeyring;
+
+DBusKeyring* _dbus_keyring_new_for_credentials (DBusCredentials *credentials,
+ const DBusString *context,
+ DBusError *error);
+DBusKeyring* _dbus_keyring_ref (DBusKeyring *keyring);
+void _dbus_keyring_unref (DBusKeyring *keyring);
+dbus_bool_t _dbus_keyring_validate_context (const DBusString *context);
+int _dbus_keyring_get_best_key (DBusKeyring *keyring,
+ DBusError *error);
+dbus_bool_t _dbus_keyring_is_for_credentials (DBusKeyring *keyring,
+ DBusCredentials *credentials);
+dbus_bool_t _dbus_keyring_get_hex_key (DBusKeyring *keyring,
+ int key_id,
+ DBusString *hex_key);
+
+
+DBUS_END_DECLS
+
+#endif /* DBUS_KEYRING_H */
diff --git a/src/3rdparty/libdbus/dbus/dbus-list.c b/src/3rdparty/libdbus/dbus/dbus-list.c
new file mode 100644
index 00000000..b3b76c75
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-list.c
@@ -0,0 +1,819 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-list.c Generic linked list utility (internal to D-Bus implementation)
+ *
+ * Copyright (C) 2002 Red Hat, Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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-list.h"
+#include "dbus-mempool.h"
+#include "dbus-threads-internal.h"
+#include <dbus/dbus-test-tap.h>
+
+/**
+ * @defgroup DBusList Linked list
+ * @ingroup DBusInternals
+ * @brief DBusList data structure
+ *
+ * Types and functions related to DBusList.
+ */
+
+/* Protected by _DBUS_LOCK (list) */
+static DBusMemPool *list_pool;
+
+/**
+ * @defgroup DBusListInternals Linked list implementation details
+ * @ingroup DBusInternals
+ * @brief DBusList implementation details
+ *
+ * The guts of DBusList.
+ *
+ * @{
+ */
+
+/* the mem pool is probably a speed hit, with the thread
+ * lock, though it does still save memory - unknown.
+ */
+static DBusList*
+alloc_link (void *data)
+{
+ DBusList *link;
+
+ if (!_DBUS_LOCK (list))
+ return FALSE;
+
+ if (list_pool == NULL)
+ {
+ list_pool = _dbus_mem_pool_new (sizeof (DBusList), TRUE);
+
+ if (list_pool == NULL)
+ {
+ _DBUS_UNLOCK (list);
+ return NULL;
+ }
+
+ link = _dbus_mem_pool_alloc (list_pool);
+ if (link == NULL)
+ {
+ _dbus_mem_pool_free (list_pool);
+ list_pool = NULL;
+ _DBUS_UNLOCK (list);
+ return NULL;
+ }
+ }
+ else
+ {
+ link = _dbus_mem_pool_alloc (list_pool);
+ }
+
+ if (link)
+ link->data = data;
+
+ _DBUS_UNLOCK (list);
+
+ return link;
+}
+
+static void
+free_link (DBusList *link)
+{
+ if (!_DBUS_LOCK (list))
+ _dbus_assert_not_reached ("we should have initialized global locks "
+ "before we allocated a linked-list link");
+
+ if (_dbus_mem_pool_dealloc (list_pool, link))
+ {
+ _dbus_mem_pool_free (list_pool);
+ list_pool = NULL;
+ }
+
+ _DBUS_UNLOCK (list);
+}
+
+static void
+link_before (DBusList **list,
+ DBusList *before_this_link,
+ DBusList *link)
+{
+ if (*list == NULL)
+ {
+ link->prev = link;
+ link->next = link;
+ *list = link;
+ }
+ else
+ {
+ link->next = before_this_link;
+ link->prev = before_this_link->prev;
+ before_this_link->prev = link;
+ link->prev->next = link;
+
+ if (before_this_link == *list)
+ *list = link;
+ }
+}
+
+static void
+link_after (DBusList **list,
+ DBusList *after_this_link,
+ DBusList *link)
+{
+ if (*list == NULL)
+ {
+ link->prev = link;
+ link->next = link;
+ *list = link;
+ }
+ else
+ {
+ link->prev = after_this_link;
+ link->next = after_this_link->next;
+ after_this_link->next = link;
+ link->next->prev = link;
+ }
+}
+
+#ifdef DBUS_ENABLE_STATS
+void
+_dbus_list_get_stats (dbus_uint32_t *in_use_p,
+ dbus_uint32_t *in_free_list_p,
+ dbus_uint32_t *allocated_p)
+{
+ if (!_DBUS_LOCK (list))
+ {
+ *in_use_p = 0;
+ *in_free_list_p = 0;
+ *allocated_p = 0;
+ return;
+ }
+
+ _dbus_mem_pool_get_stats (list_pool, in_use_p, in_free_list_p, allocated_p);
+ _DBUS_UNLOCK (list);
+}
+#endif
+
+/** @} */
+
+/**
+ * @addtogroup DBusList
+ * @{
+ */
+
+/**
+ * @struct DBusList
+ *
+ * A node in a linked list.
+ *
+ * DBusList is a circular list; that is, the tail of the list
+ * points back to the head of the list. The empty list is
+ * represented by a #NULL pointer.
+ */
+
+/**
+ * @def _dbus_list_get_next_link
+ *
+ * Gets the next link in the list, or #NULL if
+ * there are no more links. Used for iteration.
+ *
+ * @code
+ * DBusList *link;
+ * link = _dbus_list_get_first_link (&list);
+ * while (link != NULL)
+ * {
+ * printf ("value is %p\n", link->data);
+ * link = _dbus_list_get_next_link (&link);
+ * }
+ * @endcode
+ *
+ * @param list address of the list head.
+ * @param link current link.
+ * @returns the next link, or %NULL if none.
+ *
+ */
+
+/**
+ * @def _dbus_list_get_prev_link
+ *
+ * Gets the previous link in the list, or #NULL if
+ * there are no more links. Used for iteration.
+ *
+ * @code
+ * DBusList *link;
+ * link = _dbus_list_get_last_link (&list);
+ * while (link != NULL)
+ * {
+ * printf ("value is %p\n", link->data);
+ * link = _dbus_list_get_prev_link (&link);
+ * }
+ * @endcode
+ *
+ * @param list address of the list head.
+ * @param link current link.
+ * @returns the previous link, or %NULL if none.
+ *
+ */
+
+/**
+ * Allocates a linked list node. Useful for preallocating
+ * nodes and using _dbus_list_append_link() to avoid
+ * allocations.
+ *
+ * @param data the value to store in the link.
+ * @returns a newly allocated link.
+ */
+DBusList*
+_dbus_list_alloc_link (void *data)
+{
+ return alloc_link (data);
+}
+
+/**
+ * Frees a linked list node allocated with _dbus_list_alloc_link.
+ * Does not free the data in the node.
+ *
+ * @param link the list node
+ */
+void
+_dbus_list_free_link (DBusList *link)
+{
+ free_link (link);
+}
+
+
+/**
+ * Appends a value to the list. May return #FALSE
+ * if insufficient memory exists to add a list link.
+ * This is a constant-time operation.
+ *
+ * @param list address of the list head.
+ * @param data the value to append.
+ * @returns #TRUE on success.
+ */
+dbus_bool_t
+_dbus_list_append (DBusList **list,
+ void *data)
+{
+ if (!_dbus_list_prepend (list, data))
+ return FALSE;
+
+ /* Now cycle the list forward one so the prepended node is the tail */
+ *list = (*list)->next;
+
+ return TRUE;
+}
+
+/**
+ * Prepends a value to the list. May return #FALSE
+ * if insufficient memory exists to add a list link.
+ * This is a constant-time operation.
+ *
+ * @param list address of the list head.
+ * @param data the value to prepend.
+ * @returns #TRUE on success.
+ */
+dbus_bool_t
+_dbus_list_prepend (DBusList **list,
+ void *data)
+{
+ DBusList *link;
+
+ link = alloc_link (data);
+ if (link == NULL)
+ return FALSE;
+
+ link_before (list, *list, link);
+
+ return TRUE;
+}
+
+/**
+ * Appends a link to the list.
+ * Cannot fail due to out of memory.
+ * This is a constant-time operation.
+ *
+ * @param list address of the list head.
+ * @param link the link to append.
+ */
+void
+_dbus_list_append_link (DBusList **list,
+ DBusList *link)
+{
+ _dbus_list_prepend_link (list, link);
+
+ /* Now cycle the list forward one so the prepended node is the tail */
+ *list = (*list)->next;
+}
+
+/**
+ * Prepends a link to the list.
+ * Cannot fail due to out of memory.
+ * This is a constant-time operation.
+ *
+ * @param list address of the list head.
+ * @param link the link to prepend.
+ */
+void
+_dbus_list_prepend_link (DBusList **list,
+ DBusList *link)
+{
+ link_before (list, *list, link);
+}
+
+/**
+ * Inserts data into the list after the given existing link.
+ *
+ * @param list the list to modify
+ * @param after_this_link existing link to insert after, or #NULL to prepend
+ * @param data the value to insert
+ * @returns #TRUE on success, #FALSE if memory allocation fails
+ */
+dbus_bool_t
+_dbus_list_insert_after (DBusList **list,
+ DBusList *after_this_link,
+ void *data)
+{
+ DBusList *link;
+
+ if (after_this_link == NULL)
+ return _dbus_list_prepend (list, data);
+ else
+ {
+ link = alloc_link (data);
+ if (link == NULL)
+ return FALSE;
+
+ link_after (list, after_this_link, link);
+ }
+
+ return TRUE;
+}
+
+/**
+ * Inserts a link into the list before the given existing link.
+ *
+ * @param list the list to modify
+ * @param before_this_link existing link to insert before, or #NULL to append
+ * @param link the link to insert
+ */
+void
+_dbus_list_insert_before_link (DBusList **list,
+ DBusList *before_this_link,
+ DBusList *link)
+{
+ if (before_this_link == NULL)
+ _dbus_list_append_link (list, link);
+ else
+ link_before (list, before_this_link, link);
+}
+
+/**
+ * Inserts a link into the list after the given existing link.
+ *
+ * @param list the list to modify
+ * @param after_this_link existing link to insert after, or #NULL to prepend
+ * @param link the link to insert
+ */
+void
+_dbus_list_insert_after_link (DBusList **list,
+ DBusList *after_this_link,
+ DBusList *link)
+{
+ if (after_this_link == NULL)
+ _dbus_list_prepend_link (list, link);
+ else
+ link_after (list, after_this_link, link);
+}
+
+/**
+ * Removes a value from the list. Only removes the
+ * first value equal to the given data pointer,
+ * even if multiple values exist which match.
+ * This is a linear-time operation.
+ *
+ * @param list address of the list head.
+ * @param data the value to remove.
+ * @returns #TRUE if a value was found to remove.
+ */
+dbus_bool_t
+_dbus_list_remove (DBusList **list,
+ void *data)
+{
+ DBusList *link;
+
+ link = *list;
+ while (link != NULL)
+ {
+ if (link->data == data)
+ {
+ _dbus_list_remove_link (list, link);
+ return TRUE;
+ }
+
+ link = _dbus_list_get_next_link (list, link);
+ }
+
+ return FALSE;
+}
+
+/**
+ * Removes a value from the list. Only removes the
+ * last value equal to the given data pointer,
+ * even if multiple values exist which match.
+ * This is a linear-time operation.
+ *
+ * @param list address of the list head.
+ * @param data the value to remove.
+ * @returns #TRUE if a value was found to remove.
+ */
+dbus_bool_t
+_dbus_list_remove_last (DBusList **list,
+ void *data)
+{
+ DBusList *link;
+
+ link = _dbus_list_find_last (list, data);
+ if (link)
+ {
+ _dbus_list_remove_link (list, link);
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+/**
+ * Finds a value in the list. Returns the last link
+ * with value equal to the given data pointer.
+ * This is a linear-time operation.
+ * Returns #NULL if no value found that matches.
+ *
+ * @param list address of the list head.
+ * @param data the value to find.
+ * @returns the link if found
+ */
+DBusList*
+_dbus_list_find_last (DBusList **list,
+ void *data)
+{
+ DBusList *link;
+
+ link = _dbus_list_get_last_link (list);
+
+ while (link != NULL)
+ {
+ if (link->data == data)
+ return link;
+
+ link = _dbus_list_get_prev_link (list, link);
+ }
+
+ return NULL;
+}
+
+/**
+ * Removes the given link from the list, but doesn't
+ * free it. _dbus_list_remove_link() both removes the
+ * link and also frees it.
+ *
+ * @param list the list
+ * @param link the link in the list
+ */
+void
+_dbus_list_unlink (DBusList **list,
+ DBusList *link)
+{
+ if (link->next == link)
+ {
+ /* one-element list */
+ *list = NULL;
+ }
+ else
+ {
+ link->prev->next = link->next;
+ link->next->prev = link->prev;
+
+ if (*list == link)
+ *list = link->next;
+ }
+
+ link->next = NULL;
+ link->prev = NULL;
+}
+
+/**
+ * Removes a link from the list. This is a constant-time operation.
+ *
+ * @param list address of the list head.
+ * @param link the list link to remove.
+ */
+void
+_dbus_list_remove_link (DBusList **list,
+ DBusList *link)
+{
+ _dbus_list_unlink (list, link);
+ free_link (link);
+}
+
+/**
+ * Frees all links in the list and sets the list head to #NULL. Does
+ * not free the data in each link, for obvious reasons. This is a
+ * linear-time operation.
+ *
+ * @param list address of the list head.
+ */
+void
+_dbus_list_clear (DBusList **list)
+{
+ DBusList *link;
+
+ link = *list;
+ while (link != NULL)
+ {
+ DBusList *next = _dbus_list_get_next_link (list, link);
+
+ free_link (link);
+
+ link = next;
+ }
+
+ *list = NULL;
+}
+
+/**
+ * Free every link and every element in the list.
+ *
+ * @param list address of the head of the list.
+ * @param function free-function to call for each element.
+ *
+ */
+void
+_dbus_list_clear_full (DBusList **list,
+ DBusFreeFunction function)
+{
+ DBusList *link;
+
+ link = *list;
+ while (link != NULL)
+ {
+ DBusList *next = _dbus_list_get_next_link (list, link);
+
+ function (link->data);
+ free_link (link);
+
+ link = next;
+ }
+
+ *list = NULL;
+}
+
+/**
+ * Gets the first link in the list.
+ * This is a constant-time operation.
+ *
+ * @param list address of the list head.
+ * @returns the first link, or #NULL for an empty list.
+ */
+DBusList*
+_dbus_list_get_first_link (DBusList **list)
+{
+ return *list;
+}
+
+/**
+ * Gets the last link in the list.
+ * This is a constant-time operation.
+ *
+ * @param list address of the list head.
+ * @returns the last link, or #NULL for an empty list.
+ */
+DBusList*
+_dbus_list_get_last_link (DBusList **list)
+{
+ if (*list == NULL)
+ return NULL;
+ else
+ return (*list)->prev;
+}
+
+/**
+ * Gets the last data in the list.
+ * This is a constant-time operation.
+ *
+ * @param list address of the list head.
+ * @returns the last data in the list, or #NULL for an empty list.
+ */
+void*
+_dbus_list_get_last (DBusList **list)
+{
+ if (*list == NULL)
+ return NULL;
+ else
+ return (*list)->prev->data;
+}
+
+/**
+ * Gets the first data in the list.
+ * This is a constant-time operation.
+ *
+ * @param list address of the list head.
+ * @returns the first data in the list, or #NULL for an empty list.
+ */
+void*
+_dbus_list_get_first (DBusList **list)
+{
+ if (*list == NULL)
+ return NULL;
+ else
+ return (*list)->data;
+}
+
+/**
+ * Removes the first link in the list and returns it. This is a
+ * constant-time operation.
+ *
+ * @param list address of the list head.
+ * @returns the first link in the list, or #NULL for an empty list.
+ */
+DBusList*
+_dbus_list_pop_first_link (DBusList **list)
+{
+ DBusList *link;
+
+ link = _dbus_list_get_first_link (list);
+ if (link == NULL)
+ return NULL;
+
+ _dbus_list_unlink (list, link);
+
+ return link;
+}
+
+/**
+ * Removes the first value in the list and returns it. This is a
+ * constant-time operation.
+ *
+ * @param list address of the list head.
+ * @returns the first data in the list, or #NULL for an empty list.
+ */
+void*
+_dbus_list_pop_first (DBusList **list)
+{
+ DBusList *link;
+ void *data;
+
+ link = _dbus_list_get_first_link (list);
+ if (link == NULL)
+ return NULL;
+
+ data = link->data;
+ _dbus_list_remove_link (list, link);
+
+ return data;
+}
+
+/**
+ * Removes the last value in the list and returns it. This is a
+ * constant-time operation.
+ *
+ * @param list address of the list head.
+ * @returns the last data in the list, or #NULL for an empty list.
+ */
+void*
+_dbus_list_pop_last (DBusList **list)
+{
+ DBusList *link;
+ void *data;
+
+ link = _dbus_list_get_last_link (list);
+ if (link == NULL)
+ return NULL;
+
+ data = link->data;
+ _dbus_list_remove_link (list, link);
+
+ return data;
+}
+
+/**
+ * Copies a list. This is a linear-time operation. If there isn't
+ * enough memory to copy the entire list, the destination list will be
+ * set to #NULL.
+ *
+ * @param list address of the head of the list to copy.
+ * @param dest address where the copied list should be placed.
+ * @returns #TRUE on success, #FALSE if not enough memory.
+ */
+dbus_bool_t
+_dbus_list_copy (DBusList **list,
+ DBusList **dest)
+{
+ DBusList *link;
+
+ _dbus_assert (list != dest);
+
+ *dest = NULL;
+
+ link = *list;
+ while (link != NULL)
+ {
+ if (!_dbus_list_append (dest, link->data))
+ {
+ /* free what we have so far */
+ _dbus_list_clear (dest);
+ return FALSE;
+ }
+
+ link = _dbus_list_get_next_link (list, link);
+ }
+
+ return TRUE;
+}
+
+/**
+ * Gets the length of a list. This is a linear-time
+ * operation.
+ *
+ * @param list address of the head of the list
+ * @returns number of elements in the list.
+ */
+int
+_dbus_list_get_length (DBusList **list)
+{
+ DBusList *link;
+ int length;
+
+ length = 0;
+
+ link = *list;
+ while (link != NULL)
+ {
+ ++length;
+
+ link = _dbus_list_get_next_link (list, link);
+ }
+
+ return length;
+}
+
+/**
+ * Calls the given function for each element in the list. The
+ * function is passed the list element as its first argument, and the
+ * given data as its second argument.
+ *
+ * @param list address of the head of the list.
+ * @param function function to call for each element.
+ * @param data extra data for the function.
+ *
+ */
+void
+_dbus_list_foreach (DBusList **list,
+ DBusForeachFunction function,
+ void *data)
+{
+ DBusList *link;
+
+ link = *list;
+ while (link != NULL)
+ {
+ DBusList *next = _dbus_list_get_next_link (list, link);
+
+ (* function) (link->data, data);
+
+ link = next;
+ }
+}
+
+/**
+ * Check whether length is exactly one.
+ *
+ * @param list the list
+ * @returns #TRUE if length is exactly one
+ */
+dbus_bool_t
+_dbus_list_length_is_one (DBusList **list)
+{
+ return (*list != NULL &&
+ (*list)->next == *list);
+}
+
+/** @} */
diff --git a/src/3rdparty/libdbus/dbus/dbus-list.h b/src/3rdparty/libdbus/dbus/dbus-list.h
new file mode 100644
index 00000000..49217df2
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-list.h
@@ -0,0 +1,132 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-list.h Generic linked list utility (internal to D-Bus implementation)
+ *
+ * Copyright (C) 2002, 2003 Red Hat, Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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
+ *
+ */
+
+#ifndef DBUS_LIST_H
+#define DBUS_LIST_H
+
+#include <dbus/dbus-internals.h>
+#include <dbus/dbus-memory.h>
+#include <dbus/dbus-types.h>
+#include <dbus/dbus-sysdeps.h>
+
+DBUS_BEGIN_DECLS
+
+struct DBusList
+{
+ DBusList *prev; /**< Previous list node. */
+ DBusList *next; /**< Next list node. */
+ void *data; /**< Data stored at this element. */
+};
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_list_append (DBusList **list,
+ void *data);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_list_prepend (DBusList **list,
+ void *data);
+dbus_bool_t _dbus_list_insert_before (DBusList **list,
+ DBusList *before_this_link,
+ void *data);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_list_insert_after (DBusList **list,
+ DBusList *after_this_link,
+ void *data);
+DBUS_PRIVATE_EXPORT
+void _dbus_list_insert_before_link (DBusList **list,
+ DBusList *before_this_link,
+ DBusList *link);
+DBUS_PRIVATE_EXPORT
+void _dbus_list_insert_after_link (DBusList **list,
+ DBusList *after_this_link,
+ DBusList *link);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_list_remove (DBusList **list,
+ void *data);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_list_remove_last (DBusList **list,
+ void *data);
+DBUS_PRIVATE_EXPORT
+void _dbus_list_remove_link (DBusList **list,
+ DBusList *link);
+DBUS_PRIVATE_EXPORT
+DBusList* _dbus_list_find_last (DBusList **list,
+ void *data);
+DBUS_PRIVATE_EXPORT
+void _dbus_list_clear (DBusList **list);
+DBUS_PRIVATE_EXPORT
+void _dbus_list_clear_full (DBusList **list,
+ DBusFreeFunction function);
+DBUS_PRIVATE_EXPORT
+DBusList* _dbus_list_get_first_link (DBusList **list);
+DBUS_PRIVATE_EXPORT
+DBusList* _dbus_list_get_last_link (DBusList **list);
+DBUS_PRIVATE_EXPORT
+void* _dbus_list_get_last (DBusList **list);
+DBUS_PRIVATE_EXPORT
+void* _dbus_list_get_first (DBusList **list);
+DBUS_PRIVATE_EXPORT
+void* _dbus_list_pop_first (DBusList **list);
+DBUS_PRIVATE_EXPORT
+void* _dbus_list_pop_last (DBusList **list);
+DBUS_PRIVATE_EXPORT
+DBusList* _dbus_list_pop_first_link (DBusList **list);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_list_copy (DBusList **list,
+ DBusList **dest);
+DBUS_PRIVATE_EXPORT
+int _dbus_list_get_length (DBusList **list);
+DBUS_PRIVATE_EXPORT
+DBusList* _dbus_list_alloc_link (void *data);
+DBUS_PRIVATE_EXPORT
+void _dbus_list_free_link (DBusList *link);
+DBUS_PRIVATE_EXPORT
+void _dbus_list_unlink (DBusList **list,
+ DBusList *link);
+DBUS_PRIVATE_EXPORT
+void _dbus_list_append_link (DBusList **list,
+ DBusList *link);
+DBUS_PRIVATE_EXPORT
+void _dbus_list_prepend_link (DBusList **list,
+ DBusList *link);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_list_length_is_one (DBusList **list);
+
+
+DBUS_PRIVATE_EXPORT
+void _dbus_list_foreach (DBusList **list,
+ DBusForeachFunction function,
+ void *data);
+
+#define _dbus_list_get_next_link(list, link) ((link)->next == *(list) ? NULL : (link)->next)
+#define _dbus_list_get_prev_link(list, link) ((link) == *(list) ? NULL : (link)->prev)
+
+/* if DBUS_ENABLE_STATS */
+DBUS_PRIVATE_EXPORT
+void _dbus_list_get_stats (dbus_uint32_t *in_use_p,
+ dbus_uint32_t *in_free_list_p,
+ dbus_uint32_t *allocated_p);
+
+DBUS_END_DECLS
+
+#endif /* DBUS_LIST_H */
diff --git a/src/3rdparty/libdbus/dbus/dbus-macros-internal.h b/src/3rdparty/libdbus/dbus/dbus-macros-internal.h
new file mode 100644
index 00000000..3d7c0683
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-macros-internal.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright © 2010-2015 Ralf Habacker
+ * Copyright © 2015-2019 Collabora Ltd.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifdef DBUS_INSIDE_DBUS_H
+#error "You can't include dbus-macros-internal.h in the public header dbus.h"
+#endif
+
+#ifndef DBUS_MACROS_INTERNAL_H
+#define DBUS_MACROS_INTERNAL_H
+
+#include <dbus/dbus-macros.h>
+
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
+# define DBUS_EMBEDDED_TESTS_EXPORT DBUS_PRIVATE_EXPORT
+#else
+# define DBUS_EMBEDDED_TESTS_EXPORT /* nothing */
+#endif
+
+#if defined(DBUS_PRIVATE_EXPORT)
+ /* value forced by compiler command line, don't redefine */
+#elif defined(_WIN32)
+# if defined(DBUS_STATIC_BUILD)
+# define DBUS_PRIVATE_EXPORT /* no decoration */
+# elif defined(dbus_1_EXPORTS)
+# define DBUS_PRIVATE_EXPORT __declspec(dllexport)
+# else
+# define DBUS_PRIVATE_EXPORT __declspec(dllimport)
+# endif
+#elif defined(__GNUC__) && __GNUC__ >= 4
+# define DBUS_PRIVATE_EXPORT __attribute__ ((__visibility__ ("default")))
+#else
+# define DBUS_PRIVATE_EXPORT /* no decoration */
+#endif
+
+#endif
diff --git a/src/3rdparty/libdbus/dbus/dbus-macros.h b/src/3rdparty/libdbus/dbus/dbus-macros.h
new file mode 100644
index 00000000..5ca7bf0a
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-macros.h
@@ -0,0 +1,237 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-macros.h generic macros
+ *
+ * Copyright (C) 2002 Red Hat Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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
+ *
+ */
+#if !defined (DBUS_INSIDE_DBUS_H) && !defined (DBUS_COMPILATION)
+#error "Only <dbus/dbus.h> can be included directly, this file may disappear or change contents."
+#endif
+
+#ifndef DBUS_MACROS_H
+#define DBUS_MACROS_H
+
+#ifdef __cplusplus
+# define DBUS_BEGIN_DECLS extern "C" {
+# define DBUS_END_DECLS }
+#else
+# define DBUS_BEGIN_DECLS
+# define DBUS_END_DECLS
+#endif
+
+#ifndef TRUE
+# define TRUE 1
+#endif
+#ifndef FALSE
+# define FALSE 0
+#endif
+
+#ifndef NULL
+# ifdef __cplusplus
+# define NULL (0L)
+# else /* !__cplusplus */
+# define NULL ((void*) 0)
+# endif /* !__cplusplus */
+#endif
+
+#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)
+# define DBUS_DEPRECATED __attribute__ ((__deprecated__))
+#elif defined(_MSC_VER) && (_MSC_VER >= 1300)
+# define DBUS_DEPRECATED __declspec(deprecated)
+#else
+# define DBUS_DEPRECATED
+#endif
+
+#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 8)
+# define _DBUS_GNUC_EXTENSION __extension__
+#else
+# define _DBUS_GNUC_EXTENSION
+#endif
+
+#if (__GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4)) || \
+ defined(__clang__)
+#define _DBUS_GNUC_PRINTF( format_idx, arg_idx ) \
+ __attribute__((__format__ (__printf__, format_idx, arg_idx)))
+#define _DBUS_GNUC_NORETURN \
+ __attribute__((__noreturn__))
+#define _DBUS_GNUC_UNUSED \
+ __attribute__((__unused__))
+#else /* !__GNUC__ */
+#define _DBUS_GNUC_PRINTF( format_idx, arg_idx )
+#define _DBUS_GNUC_NORETURN
+#define _DBUS_GNUC_UNUSED
+#endif /* !__GNUC__ */
+
+#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 96)
+#define DBUS_MALLOC __attribute__((__malloc__))
+#else
+#define DBUS_MALLOC
+#endif
+
+#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)
+#define DBUS_ALLOC_SIZE(x) __attribute__((__alloc_size__(x)))
+#define DBUS_ALLOC_SIZE2(x,y) __attribute__((__alloc_size__(x,y)))
+#else
+#define DBUS_ALLOC_SIZE(x)
+#define DBUS_ALLOC_SIZE2(x,y)
+#endif
+
+/** @def _DBUS_WARN_UNUSED_RESULT
+ *
+ * An attribute for functions whose result must be checked by the caller.
+ *
+ * This macro is used in function declarations. Unlike gcc-specific
+ * attributes, to avoid compilation failure with MSVC it must appear
+ * somewhere before the function name in the declaration. Our preferred
+ * coding style is to place it before the return type, for example:
+ *
+ * DBUS_PRIVATE_EXPORT _DBUS_WARN_UNUSED_RESULT
+ * dbus_bool_t _dbus_user_database_lock_system (void);
+ */
+#if defined(_MSC_VER) && (_MSC_VER >= 1700)
+#define _DBUS_WARN_UNUSED_RESULT _Must_inspect_result_
+#elif (__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
+#define _DBUS_WARN_UNUSED_RESULT __attribute__((warn_unused_result))
+#else
+#define _DBUS_WARN_UNUSED_RESULT
+#endif
+
+/** @def _DBUS_GNUC_PRINTF
+ * used to tell gcc about printf format strings
+ */
+/** @def _DBUS_GNUC_NORETURN
+ * used to tell gcc about functions that never return, such as _dbus_abort()
+ */
+
+/* Normally docs are in .c files, but there isn't a .c file for this. */
+/**
+ * @defgroup DBusMacros Utility macros
+ * @ingroup DBus
+ * @brief #TRUE, #FALSE, #NULL, and so on
+ *
+ * Utility macros.
+ *
+ * @{
+ */
+
+/**
+ * @def DBUS_BEGIN_DECLS
+ *
+ * Macro used prior to declaring functions in the D-Bus header
+ * files. Expands to "extern "C"" when using a C++ compiler,
+ * and expands to nothing when using a C compiler.
+ *
+ * Please don't use this in your own code, consider it
+ * D-Bus internal.
+ */
+/**
+ * @def DBUS_END_DECLS
+ *
+ * Macro used after declaring functions in the D-Bus header
+ * files. Expands to "}" when using a C++ compiler,
+ * and expands to nothing when using a C compiler.
+ *
+ * Please don't use this in your own code, consider it
+ * D-Bus internal.
+ */
+/**
+ * @def TRUE
+ *
+ * Expands to "1"
+ */
+/**
+ * @def FALSE
+ *
+ * Expands to "0"
+ */
+/**
+ * @def NULL
+ *
+ * A null pointer, defined appropriately for C or C++.
+ */
+/**
+ * @def DBUS_DEPRECATED
+ *
+ * Tells the compiler to warn about a function or type if it's used.
+ * Code marked in this way should also be enclosed in
+ * @code
+ * #ifndef DBUS_DISABLE_DEPRECATED
+ * deprecated stuff here
+ * #endif
+ * @endcode
+ *
+ * Please don't use this in your own code, consider it
+ * D-Bus internal.
+ */
+/**
+ * @def _DBUS_GNUC_EXTENSION
+ *
+ * Tells gcc not to warn about extensions to the C standard in the
+ * following expression, even if compiling with -pedantic. Do not use
+ * this macro in your own code; please consider it to be internal to libdbus.
+ */
+
+/*
+ * @def DBUS_EXPORT
+ *
+ * Declare the following symbol as public. This is currently a noop on
+ * platforms other than Windows.
+ */
+
+#if defined(DBUS_EXPORT)
+ /* value forced by compiler command line, don't redefine */
+#elif defined(_WIN32)
+# if defined(DBUS_STATIC_BUILD)
+# define DBUS_EXPORT
+# elif defined(dbus_1_EXPORTS)
+# define DBUS_EXPORT __declspec(dllexport)
+# else
+# define DBUS_EXPORT __declspec(dllimport)
+# endif
+#elif defined(__GNUC__) && __GNUC__ >= 4
+# define DBUS_EXPORT __attribute__ ((__visibility__ ("default")))
+#else
+#define DBUS_EXPORT
+#endif
+
+/* Implementation for dbus_clear_message() etc. This is not API,
+ * do not use it directly.
+ *
+ * We're using a specific type (T ** and T *) instead of void ** and
+ * void * partly for type-safety, partly to be strict-aliasing-compliant,
+ * and partly to keep C++ compilers happy. This code is inlined into
+ * users of libdbus, so we can't rely on it having dbus' own compiler
+ * settings. */
+#define _dbus_clear_pointer_impl(T, pointer_to_pointer, destroy) \
+ do { \
+ T **_pp = (pointer_to_pointer); \
+ T *_value = *_pp; \
+ \
+ *_pp = NULL; \
+ \
+ if (_value != NULL) \
+ destroy (_value); \
+ } while (0)
+/* Not (destroy) (_value) in case destroy() is a function-like macro */
+
+/** @} */
+
+#endif /* DBUS_MACROS_H */
diff --git a/src/3rdparty/libdbus/dbus/dbus-marshal-basic.c b/src/3rdparty/libdbus/dbus/dbus-marshal-basic.c
new file mode 100644
index 00000000..64d68dae
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-marshal-basic.c
@@ -0,0 +1,1993 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-marshal-basic.c Marshalling routines for basic (primitive) types
+ *
+ * Copyright (C) 2002 CodeFactory AB
+ * Copyright (C) 2003, 2004, 2005 Red Hat, Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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-basic.h"
+#include "dbus-signature.h"
+#include <dbus/dbus-test-tap.h>
+
+#include <string.h>
+
+#define _DBUS_ASSERT_ALIGNMENT(type, op, val) \
+ _DBUS_STATIC_ASSERT (_DBUS_ALIGNOF (type) op val)
+#define _DBUS_ASSERT_CMP_ALIGNMENT(left, op, right) \
+ _DBUS_STATIC_ASSERT (_DBUS_ALIGNOF (left) op _DBUS_ALIGNOF (right))
+
+/* True by definition, but just for completeness... */
+_DBUS_STATIC_ASSERT (sizeof (char) == 1);
+_DBUS_ASSERT_ALIGNMENT (char, ==, 1);
+
+_DBUS_STATIC_ASSERT (sizeof (dbus_int16_t) == 2);
+_DBUS_ASSERT_ALIGNMENT (dbus_int16_t, <=, 2);
+_DBUS_STATIC_ASSERT (sizeof (dbus_uint16_t) == 2);
+_DBUS_ASSERT_ALIGNMENT (dbus_uint16_t, <=, 2);
+_DBUS_ASSERT_CMP_ALIGNMENT (dbus_uint16_t, ==, dbus_int16_t);
+
+_DBUS_STATIC_ASSERT (sizeof (dbus_int32_t) == 4);
+_DBUS_ASSERT_ALIGNMENT (dbus_int32_t, <=, 4);
+_DBUS_STATIC_ASSERT (sizeof (dbus_uint32_t) == 4);
+_DBUS_ASSERT_ALIGNMENT (dbus_uint32_t, <=, 4);
+_DBUS_ASSERT_CMP_ALIGNMENT (dbus_uint32_t, ==, dbus_int32_t);
+_DBUS_STATIC_ASSERT (sizeof (dbus_bool_t) == 4);
+_DBUS_ASSERT_ALIGNMENT (dbus_bool_t, <=, 4);
+_DBUS_ASSERT_CMP_ALIGNMENT (dbus_uint32_t, ==, dbus_bool_t);
+
+_DBUS_STATIC_ASSERT (sizeof (double) == 8);
+_DBUS_ASSERT_ALIGNMENT (double, <=, 8);
+/* Doubles might sometimes be more strictly aligned than int64, but we
+ * assume they are no less strictly aligned. This means every (double *)
+ * has enough alignment to be treated as though it was a
+ * (dbus_uint64_t *). */
+_DBUS_ASSERT_CMP_ALIGNMENT (dbus_uint64_t, <=, double);
+
+_DBUS_STATIC_ASSERT (sizeof (dbus_int64_t) == 8);
+_DBUS_ASSERT_ALIGNMENT (dbus_int64_t, <=, 8);
+_DBUS_STATIC_ASSERT (sizeof (dbus_uint64_t) == 8);
+_DBUS_ASSERT_ALIGNMENT (dbus_uint64_t, <=, 8);
+_DBUS_ASSERT_CMP_ALIGNMENT (dbus_uint64_t, ==, dbus_int64_t);
+
+_DBUS_STATIC_ASSERT (sizeof (DBusBasicValue) >= 8);
+/* The alignment of a DBusBasicValue might conceivably be > 8 because of the
+ * pointer, so we don't assert about it */
+
+_DBUS_STATIC_ASSERT (sizeof (DBus8ByteStruct) == 8);
+_DBUS_ASSERT_ALIGNMENT (DBus8ByteStruct, <=, 8);
+
+/**
+ * @defgroup DBusMarshal marshaling and unmarshaling
+ * @ingroup DBusInternals
+ * @brief functions to marshal/unmarshal data from the wire
+ *
+ * Types and functions related to converting primitive data types from
+ * wire format to native machine format, and vice versa.
+ *
+ * A signature is just a string with multiple types one after the other.
+ * for example a type is "i" or "(ii)", a signature is "i(ii)"
+ * where i is int and (ii) is struct { int; int; }
+ *
+ * @{
+ */
+
+static void
+pack_2_octets (dbus_uint16_t value,
+ int byte_order,
+ void *data)
+{
+ _dbus_assert (_DBUS_ALIGN_ADDRESS (data, 2) == data);
+
+ if ((byte_order) == DBUS_LITTLE_ENDIAN)
+ *((dbus_uint16_t*)(data)) = DBUS_UINT16_TO_LE (value);
+ else
+ *((dbus_uint16_t*)(data)) = DBUS_UINT16_TO_BE (value);
+}
+
+static void
+pack_4_octets (dbus_uint32_t value,
+ int byte_order,
+ void *data)
+{
+ _dbus_assert (_DBUS_ALIGN_ADDRESS (data, 4) == data);
+
+ if ((byte_order) == DBUS_LITTLE_ENDIAN)
+ *((dbus_uint32_t*)(data)) = DBUS_UINT32_TO_LE (value);
+ else
+ *((dbus_uint32_t*)(data)) = DBUS_UINT32_TO_BE (value);
+}
+
+static void
+pack_8_octets (dbus_uint64_t value,
+ int byte_order,
+ void *data)
+{
+ _dbus_assert (_DBUS_ALIGN_ADDRESS (data, 8) == data);
+
+ if ((byte_order) == DBUS_LITTLE_ENDIAN)
+ *((dbus_uint64_t*)(data)) = DBUS_UINT64_TO_LE (value);
+ else
+ *((dbus_uint64_t*)(data)) = DBUS_UINT64_TO_BE (value);
+}
+
+/**
+ * Packs a 32 bit unsigned integer into a data pointer.
+ *
+ * @param value the value
+ * @param byte_order the byte order to use
+ * @param data the data pointer
+ */
+void
+_dbus_pack_uint32 (dbus_uint32_t value,
+ int byte_order,
+ unsigned char *data)
+{
+ pack_4_octets (value, byte_order, data);
+}
+
+static void
+swap_8_octets (dbus_uint64_t *value,
+ int byte_order)
+{
+ if (byte_order != DBUS_COMPILER_BYTE_ORDER)
+ {
+ *value = DBUS_UINT64_SWAP_LE_BE (*value);
+ }
+}
+
+#ifndef _dbus_unpack_uint16
+/**
+ * Unpacks a 16 bit unsigned integer from a data pointer
+ *
+ * @param byte_order The byte order to use
+ * @param data the data pointer
+ * @returns the integer
+ */
+dbus_uint16_t
+_dbus_unpack_uint16 (int byte_order,
+ const unsigned char *data)
+{
+ _dbus_assert (_DBUS_ALIGN_ADDRESS (data, 2) == data);
+
+ if (byte_order == DBUS_LITTLE_ENDIAN)
+ return DBUS_UINT16_FROM_LE (*(dbus_uint16_t *) (void *) data);
+ else
+ return DBUS_UINT16_FROM_BE (*(dbus_uint16_t *) (void *) data);
+}
+#endif /* _dbus_unpack_uint16 */
+
+#ifndef _dbus_unpack_uint32
+/**
+ * Unpacks a 32 bit unsigned integer from a data pointer
+ *
+ * @param byte_order The byte order to use
+ * @param data the data pointer
+ * @returns the integer
+ */
+dbus_uint32_t
+_dbus_unpack_uint32 (int byte_order,
+ const unsigned char *data)
+{
+ _dbus_assert (_DBUS_ALIGN_ADDRESS (data, 4) == data);
+
+ if (byte_order == DBUS_LITTLE_ENDIAN)
+ return DBUS_UINT32_FROM_LE (*(dbus_uint32_t *) (void *) data);
+ else
+ return DBUS_UINT32_FROM_BE (*(dbus_uint32_t *) (void *) data);
+}
+#endif /* _dbus_unpack_uint32 */
+
+static void
+set_2_octets (DBusString *str,
+ int offset,
+ dbus_uint16_t value,
+ int byte_order)
+{
+ char *data;
+
+ _dbus_assert (byte_order == DBUS_LITTLE_ENDIAN ||
+ byte_order == DBUS_BIG_ENDIAN);
+
+ data = _dbus_string_get_data_len (str, offset, 2);
+
+ pack_2_octets (value, byte_order, (unsigned char *) data);
+}
+
+static void
+set_4_octets (DBusString *str,
+ int offset,
+ dbus_uint32_t value,
+ int byte_order)
+{
+ char *data;
+
+ _dbus_assert (byte_order == DBUS_LITTLE_ENDIAN ||
+ byte_order == DBUS_BIG_ENDIAN);
+
+ data = _dbus_string_get_data_len (str, offset, 4);
+
+ pack_4_octets (value, byte_order, (unsigned char *) data);
+}
+
+static void
+set_8_octets (DBusString *str,
+ int offset,
+ dbus_uint64_t value,
+ int byte_order)
+{
+ char *data;
+
+ _dbus_assert (byte_order == DBUS_LITTLE_ENDIAN ||
+ byte_order == DBUS_BIG_ENDIAN);
+
+ data = _dbus_string_get_data_len (str, offset, 8);
+
+ pack_8_octets (value, byte_order, (unsigned char *) data);
+}
+
+/**
+ * Sets the 4 bytes at the given offset to a marshaled unsigned
+ * integer, replacing anything found there previously.
+ *
+ * @param str the string to write the marshalled int to
+ * @param pos the byte offset where int should be written
+ * @param value the value
+ * @param byte_order the byte order to use
+ *
+ */
+void
+_dbus_marshal_set_uint32 (DBusString *str,
+ int pos,
+ dbus_uint32_t value,
+ int byte_order)
+{
+ set_4_octets (str, pos, value, byte_order);
+}
+
+/**
+ * Sets the existing marshaled string at the given offset with
+ * a new marshaled string. The given offset must point to
+ * an existing string or the wrong length will be deleted
+ * and replaced with the new string.
+ *
+ * Note: no attempt is made by this function to re-align
+ * any data which has been already marshalled after this
+ * string. Use with caution.
+ *
+ * @param str the string to write the marshalled string to
+ * @param pos the position of the marshaled string length
+ * @param value the value
+ * @param byte_order the byte order to use
+ * @param old_end_pos place to store byte after the nul byte of the old value
+ * @param new_end_pos place to store byte after the nul byte of the new value
+ * @returns #TRUE on success, #FALSE if no memory
+ *
+ */
+static dbus_bool_t
+set_string (DBusString *str,
+ int pos,
+ const char *value,
+ int byte_order,
+ int *old_end_pos,
+ int *new_end_pos)
+{
+ int old_len, new_len;
+ DBusString dstr;
+
+ _dbus_string_init_const (&dstr, value);
+
+ _dbus_assert (_DBUS_ALIGN_VALUE (pos, 4) == (unsigned) pos);
+ old_len = _dbus_unpack_uint32 (byte_order,
+ _dbus_string_get_const_udata_len (str, pos, 4));
+
+ new_len = _dbus_string_get_length (&dstr);
+
+ if (!_dbus_string_replace_len (&dstr, 0, new_len,
+ str, pos + 4, old_len))
+ return FALSE;
+
+ _dbus_marshal_set_uint32 (str, pos, new_len, byte_order);
+
+ if (old_end_pos)
+ *old_end_pos = pos + 4 + old_len + 1;
+ if (new_end_pos)
+ *new_end_pos = pos + 4 + new_len + 1;
+
+ return TRUE;
+}
+
+/**
+ * Sets the existing marshaled signature at the given offset to a new
+ * marshaled signature. Same basic ideas as set_string().
+ *
+ * @param str the string to write the marshalled signature to
+ * @param pos the position of the marshaled signature length
+ * @param value the value
+ * @param byte_order the byte order to use
+ * @param old_end_pos place to store byte after the nul byte of the old value
+ * @param new_end_pos place to store byte after the nul byte of the new value
+ * @returns #TRUE on success, #FALSE if no memory
+ *
+ */
+static dbus_bool_t
+set_signature (DBusString *str,
+ int pos,
+ const char *value,
+ int byte_order,
+ int *old_end_pos,
+ int *new_end_pos)
+{
+ int old_len, new_len;
+ DBusString dstr;
+
+ _dbus_string_init_const (&dstr, value);
+
+ old_len = _dbus_string_get_byte (str, pos);
+ new_len = _dbus_string_get_length (&dstr);
+
+ if (!_dbus_string_replace_len (&dstr, 0, new_len,
+ str, pos + 1, old_len))
+ return FALSE;
+
+ _dbus_string_set_byte (str, pos, new_len);
+
+ if (old_end_pos)
+ *old_end_pos = pos + 1 + old_len + 1;
+ if (new_end_pos)
+ *new_end_pos = pos + 1 + new_len + 1;
+
+ return TRUE;
+}
+
+/**
+ * Sets an existing basic type value to a new value.
+ * Arguments work the same way as _dbus_marshal_basic_type().
+ *
+ * @param str the string
+ * @param pos location of the current value
+ * @param type the type of the current and new values
+ * @param value the address of the new value
+ * @param byte_order byte order for marshaling
+ * @param old_end_pos location to store end position of the old value, or #NULL
+ * @param new_end_pos location to store end position of the new value, or #NULL
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_marshal_set_basic (DBusString *str,
+ int pos,
+ int type,
+ const void *value,
+ int byte_order,
+ int *old_end_pos,
+ int *new_end_pos)
+{
+ /* Static assertions near the top of this file assert that signed and
+ * unsigned 16- and 32-bit quantities have the same alignment, and that
+ * doubles have alignment at least as strict as unsigned int64, so we
+ * don't have to distinguish further: every (double *)
+ * has strong enough alignment to be treated as though it was a
+ * (dbus_uint64_t *). Going via a (void *) means the compiler should
+ * know that pointers can alias each other. */
+ const unsigned char *u8_p;
+ const dbus_uint16_t *u16_p;
+ const dbus_uint32_t *u32_p;
+ const dbus_uint64_t *u64_p;
+ const char * const *string_p;
+
+ switch (type)
+ {
+ case DBUS_TYPE_BYTE:
+ u8_p = value;
+ _dbus_string_set_byte (str, pos, *u8_p);
+ if (old_end_pos)
+ *old_end_pos = pos + 1;
+ if (new_end_pos)
+ *new_end_pos = pos + 1;
+ return TRUE;
+ break;
+ case DBUS_TYPE_INT16:
+ case DBUS_TYPE_UINT16:
+ u16_p = value;
+ pos = _DBUS_ALIGN_VALUE (pos, 2);
+ set_2_octets (str, pos, *u16_p, byte_order);
+ if (old_end_pos)
+ *old_end_pos = pos + 2;
+ if (new_end_pos)
+ *new_end_pos = pos + 2;
+ return TRUE;
+ break;
+ case DBUS_TYPE_BOOLEAN:
+ case DBUS_TYPE_INT32:
+ case DBUS_TYPE_UINT32:
+ case DBUS_TYPE_UNIX_FD:
+ u32_p = value;
+ pos = _DBUS_ALIGN_VALUE (pos, 4);
+ set_4_octets (str, pos, *u32_p, byte_order);
+ if (old_end_pos)
+ *old_end_pos = pos + 4;
+ if (new_end_pos)
+ *new_end_pos = pos + 4;
+ return TRUE;
+ break;
+ case DBUS_TYPE_INT64:
+ case DBUS_TYPE_UINT64:
+ case DBUS_TYPE_DOUBLE:
+ u64_p = value;
+ pos = _DBUS_ALIGN_VALUE (pos, 8);
+ set_8_octets (str, pos, *u64_p, byte_order);
+ if (old_end_pos)
+ *old_end_pos = pos + 8;
+ if (new_end_pos)
+ *new_end_pos = pos + 8;
+ return TRUE;
+ break;
+ case DBUS_TYPE_STRING:
+ case DBUS_TYPE_OBJECT_PATH:
+ string_p = value;
+ pos = _DBUS_ALIGN_VALUE (pos, 4);
+ _dbus_assert (*string_p != NULL);
+ return set_string (str, pos, *string_p, byte_order,
+ old_end_pos, new_end_pos);
+ break;
+ case DBUS_TYPE_SIGNATURE:
+ string_p = value;
+ _dbus_assert (*string_p != NULL);
+ return set_signature (str, pos, *string_p, byte_order,
+ old_end_pos, new_end_pos);
+ break;
+ default:
+ _dbus_assert_not_reached ("not a basic type");
+ return FALSE;
+ break;
+ }
+}
+
+/**
+ * Convenience function to demarshal a 32 bit unsigned integer.
+ *
+ * @param str the string containing the data
+ * @param byte_order the byte order
+ * @param pos the position in the string
+ * @param new_pos the new position of the string
+ * @returns the demarshaled integer.
+ */
+dbus_uint32_t
+_dbus_marshal_read_uint32 (const DBusString *str,
+ int pos,
+ int byte_order,
+ int *new_pos)
+{
+ pos = _DBUS_ALIGN_VALUE (pos, 4);
+
+ if (new_pos)
+ *new_pos = pos + 4;
+
+ _dbus_assert (pos + 4 <= _dbus_string_get_length (str));
+
+ return _dbus_unpack_uint32 (byte_order,
+ _dbus_string_get_const_udata (str) + pos);
+}
+
+/**
+ * Demarshals a basic-typed value. The "value" pointer is always
+ * the address of a variable of the basic type. So e.g.
+ * if the basic type is "double" then the pointer is
+ * a double*, and if it's "char*" then the pointer is
+ * a "char**".
+ *
+ * A value of type #DBusBasicValue is guaranteed to be large enough to
+ * hold any of the types that may be returned, which is handy if you
+ * are trying to do things generically. For example you can pass
+ * a DBusBasicValue* in to this function, and then pass the same
+ * DBusBasicValue* in to _dbus_marshal_basic_type() in order to
+ * move a value from one place to another.
+ *
+ * @param str the string containing the data
+ * @param pos position in the string
+ * @param type type of value to demarshal
+ * @param value pointer to return value data
+ * @param byte_order the byte order
+ * @param new_pos pointer to update with new position, or #NULL
+ **/
+void
+_dbus_marshal_read_basic (const DBusString *str,
+ int pos,
+ int type,
+ void *value,
+ int byte_order,
+ int *new_pos)
+{
+ const char *str_data;
+
+ _dbus_assert (dbus_type_is_basic (type));
+
+ str_data = _dbus_string_get_const_data (str);
+
+ /* Below we volatile types to avoid aliasing issues;
+ * see http://bugs.freedesktop.org/show_bug.cgi?id=20137
+ */
+
+ switch (type)
+ {
+ case DBUS_TYPE_BYTE:
+ {
+ volatile unsigned char *vp = value;
+ *vp = (unsigned char) _dbus_string_get_byte (str, pos);
+ (pos)++;
+ }
+ break;
+ case DBUS_TYPE_INT16:
+ case DBUS_TYPE_UINT16:
+ {
+ volatile dbus_uint16_t *vp = value;
+ pos = _DBUS_ALIGN_VALUE (pos, 2);
+ *vp = *(dbus_uint16_t *) (void *) (str_data + pos);
+ if (byte_order != DBUS_COMPILER_BYTE_ORDER)
+ *vp = DBUS_UINT16_SWAP_LE_BE (*vp);
+ pos += 2;
+ }
+ break;
+ case DBUS_TYPE_INT32:
+ case DBUS_TYPE_UINT32:
+ case DBUS_TYPE_BOOLEAN:
+ case DBUS_TYPE_UNIX_FD:
+ {
+ volatile dbus_uint32_t *vp = value;
+ pos = _DBUS_ALIGN_VALUE (pos, 4);
+ *vp = *(dbus_uint32_t *) (void *) (str_data + pos);
+ if (byte_order != DBUS_COMPILER_BYTE_ORDER)
+ *vp = DBUS_UINT32_SWAP_LE_BE (*vp);
+ pos += 4;
+ }
+ break;
+ case DBUS_TYPE_INT64:
+ case DBUS_TYPE_UINT64:
+ case DBUS_TYPE_DOUBLE:
+ {
+ volatile dbus_uint64_t *vp = value;
+ pos = _DBUS_ALIGN_VALUE (pos, 8);
+ if (byte_order != DBUS_COMPILER_BYTE_ORDER)
+ *vp = DBUS_UINT64_SWAP_LE_BE (
+ *(dbus_uint64_t *) (void *) (str_data + pos));
+ else
+ *vp = *(dbus_uint64_t *) (void *) (str_data + pos);
+ pos += 8;
+ }
+ break;
+ case DBUS_TYPE_STRING:
+ case DBUS_TYPE_OBJECT_PATH:
+ {
+ int len;
+ volatile char **vp = value;
+
+ len = _dbus_marshal_read_uint32 (str, pos, byte_order, &pos);
+
+ *vp = (char*) str_data + pos;
+
+ pos += len + 1; /* length plus nul */
+ }
+ break;
+ case DBUS_TYPE_SIGNATURE:
+ {
+ int len;
+ volatile char **vp = value;
+
+ len = _dbus_string_get_byte (str, pos);
+ pos += 1;
+
+ *vp = (char*) str_data + pos;
+
+ pos += len + 1; /* length plus nul */
+ }
+ break;
+ default:
+ _dbus_warn_check_failed ("type %s %d not a basic type",
+ _dbus_type_to_string (type), type);
+ _dbus_assert_not_reached ("not a basic type");
+ break;
+ }
+
+ if (new_pos)
+ *new_pos = pos;
+}
+
+static dbus_bool_t
+marshal_2_octets (DBusString *str,
+ int insert_at,
+ dbus_uint16_t value,
+ int byte_order,
+ int *pos_after)
+{
+ dbus_bool_t retval;
+ int orig_len;
+
+ _DBUS_STATIC_ASSERT (sizeof (value) == 2);
+
+ if (byte_order != DBUS_COMPILER_BYTE_ORDER)
+ value = DBUS_UINT16_SWAP_LE_BE (value);
+
+ orig_len = _dbus_string_get_length (str);
+
+ retval = _dbus_string_insert_2_aligned (str, insert_at,
+ (const unsigned char *)&value);
+
+ if (pos_after)
+ {
+ *pos_after = insert_at + (_dbus_string_get_length (str) - orig_len);
+ _dbus_assert (*pos_after <= _dbus_string_get_length (str));
+ }
+
+ return retval;
+}
+
+static dbus_bool_t
+marshal_4_octets (DBusString *str,
+ int insert_at,
+ dbus_uint32_t value,
+ int byte_order,
+ int *pos_after)
+{
+ dbus_bool_t retval;
+ int orig_len;
+
+ _DBUS_STATIC_ASSERT (sizeof (value) == 4);
+
+ if (byte_order != DBUS_COMPILER_BYTE_ORDER)
+ value = DBUS_UINT32_SWAP_LE_BE (value);
+
+ orig_len = _dbus_string_get_length (str);
+
+ retval = _dbus_string_insert_4_aligned (str, insert_at,
+ (const unsigned char *)&value);
+
+ if (pos_after)
+ {
+ *pos_after = insert_at + (_dbus_string_get_length (str) - orig_len);
+ _dbus_assert (*pos_after <= _dbus_string_get_length (str));
+ }
+
+ return retval;
+}
+
+static dbus_bool_t
+marshal_8_octets (DBusString *str,
+ int insert_at,
+ dbus_uint64_t value,
+ int byte_order,
+ int *pos_after)
+{
+ dbus_bool_t retval;
+ int orig_len;
+
+ _DBUS_STATIC_ASSERT (sizeof (value) == 8);
+
+ swap_8_octets (&value, byte_order);
+
+ orig_len = _dbus_string_get_length (str);
+
+ retval = _dbus_string_insert_8_aligned (str, insert_at,
+ (const unsigned char *)&value);
+
+ if (pos_after)
+ *pos_after = insert_at + _dbus_string_get_length (str) - orig_len;
+
+ return retval;
+}
+
+enum
+ {
+ MARSHAL_AS_STRING,
+ MARSHAL_AS_SIGNATURE,
+ MARSHAL_AS_BYTE_ARRAY
+ };
+
+static dbus_bool_t
+marshal_len_followed_by_bytes (int marshal_as,
+ DBusString *str,
+ int insert_at,
+ const unsigned char *value,
+ int data_len, /* doesn't include nul if any */
+ int byte_order,
+ int *pos_after)
+{
+ int pos;
+ DBusString value_str;
+ int value_len;
+
+ _dbus_assert (byte_order == DBUS_LITTLE_ENDIAN || byte_order == DBUS_BIG_ENDIAN);
+ if (insert_at > _dbus_string_get_length (str))
+ _dbus_warn ("insert_at = %d string len = %d data_len = %d",
+ insert_at, _dbus_string_get_length (str), data_len);
+
+ if (marshal_as == MARSHAL_AS_BYTE_ARRAY)
+ value_len = data_len;
+ else
+ value_len = data_len + 1; /* value has a nul */
+
+ _dbus_string_init_const_len (&value_str, (const char *) value, value_len);
+
+ pos = insert_at;
+
+ if (marshal_as == MARSHAL_AS_SIGNATURE)
+ {
+ _dbus_assert (data_len <= DBUS_MAXIMUM_SIGNATURE_LENGTH);
+ _dbus_assert (data_len <= 255); /* same as max sig len right now */
+
+ if (!_dbus_string_insert_byte (str, pos, data_len))
+ goto oom;
+
+ pos += 1;
+ }
+ else
+ {
+ if (!marshal_4_octets (str, pos, data_len,
+ byte_order, &pos))
+ goto oom;
+ }
+
+ if (!_dbus_string_copy_len (&value_str, 0, value_len,
+ str, pos))
+ goto oom;
+
+#if 0
+ /* too expensive */
+ _dbus_assert (_dbus_string_equal_substring (&value_str, 0, value_len,
+ str, pos));
+ _dbus_verbose_bytes_of_string (str, pos, value_len);
+#endif
+
+ pos += value_len;
+
+ if (pos_after)
+ *pos_after = pos;
+
+ return TRUE;
+
+ oom:
+ /* Delete what we've inserted */
+ _dbus_string_delete (str, insert_at, pos - insert_at);
+
+ return FALSE;
+}
+
+static dbus_bool_t
+marshal_string (DBusString *str,
+ int insert_at,
+ const char *value,
+ int byte_order,
+ int *pos_after)
+{
+ return marshal_len_followed_by_bytes (MARSHAL_AS_STRING,
+ str, insert_at, (const unsigned char *) value,
+ strlen (value),
+ byte_order, pos_after);
+}
+
+static dbus_bool_t
+marshal_signature (DBusString *str,
+ int insert_at,
+ const char *value,
+ int *pos_after)
+{
+ return marshal_len_followed_by_bytes (MARSHAL_AS_SIGNATURE,
+ str, insert_at, (const unsigned char *) value,
+ strlen (value),
+ DBUS_COMPILER_BYTE_ORDER, /* irrelevant */
+ pos_after);
+}
+
+/**
+ * Marshals a basic-typed value. The "value" pointer is always the
+ * address of a variable containing the basic type value.
+ * So for example for int32 it will be dbus_int32_t*, and
+ * for string it will be const char**. This is for symmetry
+ * with _dbus_marshal_read_basic() and to have a simple
+ * consistent rule.
+ *
+ * @param str string to marshal to
+ * @param insert_at where to insert the value
+ * @param type type of value
+ * @param value pointer to a variable containing the value
+ * @param byte_order byte order
+ * @param pos_after #NULL or the position after the type
+ * @returns #TRUE on success
+ **/
+dbus_bool_t
+_dbus_marshal_write_basic (DBusString *str,
+ int insert_at,
+ int type,
+ const void *value,
+ int byte_order,
+ int *pos_after)
+{
+ /* Static assertions near the top of this file assert that signed and
+ * unsigned 16- and 32-bit quantities have the same alignment, and that
+ * doubles have alignment at least as strict as unsigned int64, so we
+ * don't have to distinguish further: every (double *)
+ * has strong enough alignment to be treated as though it was a
+ * (dbus_uint64_t *). Going via a (void *) means the compiler should
+ * know that pointers can alias each other. */
+ const unsigned char *u8_p;
+ const dbus_uint16_t *u16_p;
+ const dbus_uint32_t *u32_p;
+ const dbus_uint64_t *u64_p;
+ const char * const *string_p;
+
+ _dbus_assert (dbus_type_is_basic (type));
+
+ switch (type)
+ {
+ case DBUS_TYPE_BYTE:
+ u8_p = value;
+ if (!_dbus_string_insert_byte (str, insert_at, *u8_p))
+ return FALSE;
+ if (pos_after)
+ *pos_after = insert_at + 1;
+ return TRUE;
+ break;
+ case DBUS_TYPE_INT16:
+ case DBUS_TYPE_UINT16:
+ u16_p = value;
+ return marshal_2_octets (str, insert_at, *u16_p,
+ byte_order, pos_after);
+ break;
+ case DBUS_TYPE_BOOLEAN:
+ u32_p = value;
+ return marshal_4_octets (str, insert_at, (*u32_p != FALSE),
+ byte_order, pos_after);
+ break;
+ case DBUS_TYPE_INT32:
+ case DBUS_TYPE_UINT32:
+ case DBUS_TYPE_UNIX_FD:
+ u32_p = value;
+ return marshal_4_octets (str, insert_at, *u32_p,
+ byte_order, pos_after);
+ break;
+ case DBUS_TYPE_INT64:
+ case DBUS_TYPE_UINT64:
+ case DBUS_TYPE_DOUBLE:
+ u64_p = value;
+ return marshal_8_octets (str, insert_at, *u64_p, byte_order, pos_after);
+ break;
+
+ case DBUS_TYPE_STRING:
+ case DBUS_TYPE_OBJECT_PATH:
+ string_p = value;
+ _dbus_assert (*string_p != NULL);
+ return marshal_string (str, insert_at, *string_p, byte_order, pos_after);
+ break;
+ case DBUS_TYPE_SIGNATURE:
+ string_p = value;
+ _dbus_assert (*string_p != NULL);
+ return marshal_signature (str, insert_at, *string_p, pos_after);
+ break;
+ default:
+ _dbus_assert_not_reached ("not a basic type");
+ return FALSE;
+ break;
+ }
+}
+
+static dbus_bool_t
+marshal_1_octets_array (DBusString *str,
+ int insert_at,
+ const unsigned char *value,
+ int n_elements,
+ int byte_order,
+ int *pos_after)
+{
+ int pos;
+ DBusString value_str;
+
+ _dbus_string_init_const_len (&value_str, (const char *) value, n_elements);
+
+ pos = insert_at;
+
+ if (!_dbus_string_copy_len (&value_str, 0, n_elements,
+ str, pos))
+ return FALSE;
+
+ pos += n_elements;
+
+ if (pos_after)
+ *pos_after = pos;
+
+ return TRUE;
+}
+
+/**
+ * Swaps the elements of an array to the opposite byte order
+ *
+ * @param data start of array
+ * @param n_elements number of elements
+ * @param alignment size of each element
+ */
+void
+_dbus_swap_array (unsigned char *data,
+ int n_elements,
+ int alignment)
+{
+ void *d;
+ void *end;
+
+ _dbus_assert (_DBUS_ALIGN_ADDRESS (data, alignment) == data);
+
+ /* we use const_data and cast it off so DBusString can be a const string
+ * for the unit tests. don't ask.
+ */
+ d = data;
+ end = data + (n_elements * alignment);
+
+ if (alignment == 8)
+ {
+ while (d != end)
+ {
+ *((dbus_uint64_t*)d) = DBUS_UINT64_SWAP_LE_BE (*((dbus_uint64_t*)d));
+ d = ((unsigned char *) d) + 8;
+ }
+ }
+ else if (alignment == 4)
+ {
+ while (d != end)
+ {
+ *((dbus_uint32_t*)d) = DBUS_UINT32_SWAP_LE_BE (*((dbus_uint32_t*)d));
+ d = ((unsigned char *) d) + 4;
+ }
+ }
+ else
+ {
+ _dbus_assert (alignment == 2);
+
+ while (d != end)
+ {
+ *((dbus_uint16_t*)d) = DBUS_UINT16_SWAP_LE_BE (*((dbus_uint16_t*)d));
+ d = ((unsigned char *) d) + 2;
+ }
+ }
+}
+
+static void
+swap_array (DBusString *str,
+ int array_start,
+ int n_elements,
+ int byte_order,
+ int alignment)
+{
+ _dbus_assert (_DBUS_ALIGN_VALUE (array_start, alignment) == (unsigned) array_start);
+
+ if (byte_order != DBUS_COMPILER_BYTE_ORDER)
+ {
+ /* we use const_data and cast it off so DBusString can be a const string
+ * for the unit tests. don't ask.
+ */
+ _dbus_swap_array ((unsigned char*) (_dbus_string_get_const_data (str) + array_start),
+ n_elements, alignment);
+ }
+}
+
+static dbus_bool_t
+marshal_fixed_multi (DBusString *str,
+ int insert_at,
+ const void *value,
+ int n_elements,
+ int byte_order,
+ int alignment,
+ int *pos_after)
+{
+ int old_string_len;
+ int array_start;
+ DBusString t;
+ int len_in_bytes;
+
+ _dbus_assert (n_elements <= DBUS_MAXIMUM_ARRAY_LENGTH / alignment);
+
+ old_string_len = _dbus_string_get_length (str);
+
+ len_in_bytes = n_elements * alignment;
+ array_start = insert_at;
+
+ /* Note that we do alignment padding unconditionally
+ * even if the array is empty; this means that
+ * padding + len is always equal to the number of bytes
+ * in the array.
+ */
+
+ if (!_dbus_string_insert_alignment (str, &array_start, alignment))
+ goto error;
+
+ _dbus_string_init_const_len (&t,
+ (const char *) value,
+ len_in_bytes);
+
+ if (!_dbus_string_copy (&t, 0,
+ str, array_start))
+ goto error;
+
+ swap_array (str, array_start, n_elements, byte_order, alignment);
+
+ if (pos_after)
+ *pos_after = array_start + len_in_bytes;
+
+ return TRUE;
+
+ error:
+ _dbus_string_delete (str, insert_at,
+ _dbus_string_get_length (str) - old_string_len);
+
+ return FALSE;
+}
+
+/**
+ * Marshals a block of values of fixed-length type all at once, as an
+ * optimization. dbus_type_is_fixed() returns #TRUE for fixed-length
+ * types, which are the basic types minus the string-like types.
+ *
+ * The value argument should be the adddress of an
+ * array, so e.g. "const dbus_uint32_t**"
+ *
+ * @param str string to marshal to
+ * @param insert_at where to insert the value
+ * @param element_type type of array elements
+ * @param value address of an array to marshal
+ * @param n_elements number of elements in the array
+ * @param byte_order byte order
+ * @param pos_after #NULL or the position after the type
+ * @returns #TRUE on success
+ **/
+dbus_bool_t
+_dbus_marshal_write_fixed_multi (DBusString *str,
+ int insert_at,
+ int element_type,
+ const void *value,
+ int n_elements,
+ int byte_order,
+ int *pos_after)
+{
+ /* Static assertions near the top of this file assert that signed and
+ * unsigned 16- and 32-bit quantities have the same alignment, and that
+ * doubles have alignment at least as strict as unsigned int64, so we
+ * don't have to distinguish further: every (double *)
+ * has strong enough alignment to be treated as though it was a
+ * (dbus_uint64_t *). Going via a (void *) means the compiler should
+ * know that pointers can alias each other. */
+ const unsigned char * const *u8_pp;
+ const dbus_uint16_t * const *u16_pp;
+ const dbus_uint32_t * const *u32_pp;
+ const dbus_uint64_t * const *u64_pp;
+
+ _dbus_assert (dbus_type_is_fixed (element_type));
+ _dbus_assert (n_elements >= 0);
+
+#if 0
+ _dbus_verbose ("writing %d elements of %s\n",
+ n_elements, _dbus_type_to_string (element_type));
+#endif
+
+ switch (element_type)
+ {
+ case DBUS_TYPE_BYTE:
+ u8_pp = value;
+ return marshal_1_octets_array (str, insert_at, *u8_pp, n_elements, byte_order, pos_after);
+ break;
+ case DBUS_TYPE_INT16:
+ case DBUS_TYPE_UINT16:
+ u16_pp = value;
+ return marshal_fixed_multi (str, insert_at, *u16_pp, n_elements, byte_order, 2, pos_after);
+ case DBUS_TYPE_BOOLEAN:
+ case DBUS_TYPE_INT32:
+ case DBUS_TYPE_UINT32:
+ case DBUS_TYPE_UNIX_FD:
+ u32_pp = value;
+ return marshal_fixed_multi (str, insert_at, *u32_pp, n_elements, byte_order, 4, pos_after);
+ break;
+ case DBUS_TYPE_INT64:
+ case DBUS_TYPE_UINT64:
+ case DBUS_TYPE_DOUBLE:
+ u64_pp = value;
+ return marshal_fixed_multi (str, insert_at, *u64_pp, n_elements, byte_order, 8, pos_after);
+ break;
+
+ default:
+ _dbus_assert_not_reached ("non fixed type in array write");
+ break;
+ }
+
+ return FALSE;
+}
+
+
+/**
+ * Skips over a basic-typed value, reporting the following position.
+ *
+ * @param str the string containing the data
+ * @param type type of value to read
+ * @param byte_order the byte order
+ * @param pos pointer to position in the string,
+ * updated on return to new position
+ **/
+void
+_dbus_marshal_skip_basic (const DBusString *str,
+ int type,
+ int byte_order,
+ int *pos)
+{
+ _dbus_assert (byte_order == DBUS_LITTLE_ENDIAN ||
+ byte_order == DBUS_BIG_ENDIAN);
+
+ switch (type)
+ {
+ case DBUS_TYPE_BYTE:
+ (*pos)++;
+ break;
+ case DBUS_TYPE_INT16:
+ case DBUS_TYPE_UINT16:
+ /* Advance to the next suitably-aligned position >= *pos */
+ *pos = _DBUS_ALIGN_VALUE (*pos, 2);
+ *pos += 2;
+ break;
+ case DBUS_TYPE_BOOLEAN:
+ case DBUS_TYPE_INT32:
+ case DBUS_TYPE_UINT32:
+ case DBUS_TYPE_UNIX_FD:
+ *pos = _DBUS_ALIGN_VALUE (*pos, 4);
+ *pos += 4;
+ break;
+ case DBUS_TYPE_INT64:
+ case DBUS_TYPE_UINT64:
+ case DBUS_TYPE_DOUBLE:
+ *pos = _DBUS_ALIGN_VALUE (*pos, 8);
+ *pos += 8;
+ break;
+ case DBUS_TYPE_STRING:
+ case DBUS_TYPE_OBJECT_PATH:
+ {
+ int len;
+
+ /* Let len be the number of bytes of string data, and advance
+ * *pos to just after the length */
+ len = _dbus_marshal_read_uint32 (str, *pos, byte_order, pos);
+
+ *pos += len + 1; /* length plus nul */
+ }
+ break;
+ case DBUS_TYPE_SIGNATURE:
+ {
+ int len;
+
+ len = _dbus_string_get_byte (str, *pos);
+
+ *pos += len + 2; /* length byte plus length plus nul */
+ }
+ break;
+ default:
+ _dbus_warn ("type %s not a basic type",
+ _dbus_type_to_string (type));
+ _dbus_assert_not_reached ("not a basic type");
+ break;
+ }
+
+ /* We had better still be in-bounds at this point (pointing either into
+ * the content of the string, or 1 past the logical length of the string) */
+ _dbus_assert (*pos <= _dbus_string_get_length (str));
+}
+
+/**
+ * Skips an array, returning the next position.
+ *
+ * @param str the string containing the data
+ * @param element_type the type of array elements
+ * @param byte_order the byte order
+ * @param pos pointer to position in the string,
+ * updated on return to new position
+ */
+void
+_dbus_marshal_skip_array (const DBusString *str,
+ int element_type,
+ int byte_order,
+ int *pos)
+{
+ dbus_uint32_t array_len;
+ int i;
+ int alignment;
+
+ /* Advance to the next 4-byte-aligned position >= *pos */
+ i = _DBUS_ALIGN_VALUE (*pos, 4);
+
+ /* Let array_len be the number of bytes of array data, and advance
+ * i to just after the length */
+ array_len = _dbus_marshal_read_uint32 (str, i, byte_order, &i);
+
+ /* If the element type is more strictly-aligned than the length,
+ * advance i to the next suitably-aligned position
+ * (in other words, skip the padding) */
+ alignment = _dbus_type_get_alignment (element_type);
+
+ i = _DBUS_ALIGN_VALUE (i, alignment);
+
+ /* Skip the actual array data */
+ *pos = i + array_len;
+
+ /* We had better still be in-bounds at this point (pointing either into
+ * the content of the string, or 1 past the logical length of the string) */
+ _dbus_assert (*pos <= _dbus_string_get_length (str));
+}
+
+/**
+ * Gets the alignment requirement for the given type;
+ * will be 1, 2, 4, or 8.
+ *
+ * @param typecode the type
+ * @returns alignment of 1, 2, 4, or 8
+ */
+int
+_dbus_type_get_alignment (int typecode)
+{
+ switch (typecode)
+ {
+ case DBUS_TYPE_BYTE:
+ case DBUS_TYPE_VARIANT:
+ case DBUS_TYPE_SIGNATURE:
+ return 1;
+ case DBUS_TYPE_INT16:
+ case DBUS_TYPE_UINT16:
+ return 2;
+ case DBUS_TYPE_BOOLEAN:
+ case DBUS_TYPE_INT32:
+ case DBUS_TYPE_UINT32:
+ case DBUS_TYPE_UNIX_FD:
+ /* this stuff is 4 since it starts with a length */
+ case DBUS_TYPE_STRING:
+ case DBUS_TYPE_OBJECT_PATH:
+ case DBUS_TYPE_ARRAY:
+ return 4;
+ case DBUS_TYPE_INT64:
+ case DBUS_TYPE_UINT64:
+ case DBUS_TYPE_DOUBLE:
+ /* struct is 8 since it could contain an 8-aligned item
+ * and it's simpler to just always align structs to 8;
+ * we want the amount of padding in a struct of a given
+ * type to be predictable, not location-dependent.
+ * DICT_ENTRY is always the same as struct.
+ */
+ case DBUS_TYPE_STRUCT:
+ case DBUS_TYPE_DICT_ENTRY:
+ return 8;
+
+ default:
+ _dbus_assert_not_reached ("unknown typecode in _dbus_type_get_alignment()");
+ return 0;
+ }
+}
+
+/**
+ * Returns a string describing the given type.
+ *
+ * @param typecode the type to describe
+ * @returns a constant string describing the type
+ */
+const char *
+_dbus_type_to_string (int typecode)
+{
+ switch (typecode)
+ {
+ case DBUS_TYPE_INVALID:
+ return "invalid";
+ case DBUS_TYPE_BOOLEAN:
+ return "boolean";
+ case DBUS_TYPE_BYTE:
+ return "byte";
+ case DBUS_TYPE_INT16:
+ return "int16";
+ case DBUS_TYPE_UINT16:
+ return "uint16";
+ case DBUS_TYPE_INT32:
+ return "int32";
+ case DBUS_TYPE_UINT32:
+ return "uint32";
+ case DBUS_TYPE_INT64:
+ return "int64";
+ case DBUS_TYPE_UINT64:
+ return "uint64";
+ case DBUS_TYPE_DOUBLE:
+ return "double";
+ case DBUS_TYPE_STRING:
+ return "string";
+ case DBUS_TYPE_OBJECT_PATH:
+ return "object_path";
+ case DBUS_TYPE_SIGNATURE:
+ return "signature";
+ case DBUS_TYPE_STRUCT:
+ return "struct";
+ case DBUS_TYPE_DICT_ENTRY:
+ return "dict_entry";
+ case DBUS_TYPE_ARRAY:
+ return "array";
+ case DBUS_TYPE_VARIANT:
+ return "variant";
+ case DBUS_STRUCT_BEGIN_CHAR:
+ return "begin_struct";
+ case DBUS_STRUCT_END_CHAR:
+ return "end_struct";
+ case DBUS_DICT_ENTRY_BEGIN_CHAR:
+ return "begin_dict_entry";
+ case DBUS_DICT_ENTRY_END_CHAR:
+ return "end_dict_entry";
+ case DBUS_TYPE_UNIX_FD:
+ return "unix_fd";
+ default:
+ return "unknown";
+ }
+}
+
+/**
+ * If in verbose mode, print a block of binary data.
+ *
+ * @param data the data
+ * @param len the length of the data
+ * @param offset where to start counting for byte indexes
+ */
+void
+_dbus_verbose_bytes (const unsigned char *data,
+ int len,
+ int offset)
+{
+ int i;
+ const unsigned char *aligned;
+
+ _dbus_assert (len >= 0);
+
+ if (!_dbus_is_verbose())
+ return;
+
+ /* Print blanks on first row if appropriate */
+ aligned = _DBUS_ALIGN_ADDRESS (data, 4);
+ if (aligned > data)
+ aligned -= 4;
+ _dbus_assert (aligned <= data);
+
+ if (aligned != data)
+ {
+ _dbus_verbose ("%4ld\t%p: ", - (long)(data - aligned), aligned);
+ while (aligned != data)
+ {
+ _dbus_verbose (" ");
+ ++aligned;
+ }
+ }
+
+ /* now print the bytes */
+ i = 0;
+ while (i < len)
+ {
+ if (_DBUS_ALIGN_ADDRESS (&data[i], 4) == &data[i])
+ {
+ _dbus_verbose ("%4d\t%p: ",
+ offset + i, &data[i]);
+ }
+
+ if (data[i] >= 32 &&
+ data[i] <= 126)
+ _dbus_verbose (" '%c' ", data[i]);
+ else
+ _dbus_verbose ("0x%s%x ",
+ data[i] <= 0xf ? "0" : "", data[i]);
+
+ ++i;
+
+ if (_DBUS_ALIGN_ADDRESS (&data[i], 4) == &data[i])
+ {
+ if (i > 3)
+ _dbus_verbose ("BE: %d LE: %d",
+ _dbus_unpack_uint32 (DBUS_BIG_ENDIAN, &data[i-4]),
+ _dbus_unpack_uint32 (DBUS_LITTLE_ENDIAN, &data[i-4]));
+
+ if (i > 7 &&
+ _DBUS_ALIGN_ADDRESS (&data[i], 8) == &data[i])
+ {
+ _dbus_verbose (" u64: 0x%" DBUS_INT64_MODIFIER "x",
+ *(dbus_uint64_t *) (void *) &data[i - 8]);
+ _dbus_verbose (" dbl: %g", *(double *) (void *) &data[i - 8]);
+ }
+
+ _dbus_verbose ("\n");
+ }
+ }
+
+ _dbus_verbose ("\n");
+}
+
+/**
+ * Dump the given part of the string to verbose log.
+ *
+ * @param str the string
+ * @param start the start of range to dump
+ * @param len length of range
+ */
+void
+_dbus_verbose_bytes_of_string (const DBusString *str,
+ int start,
+ int len)
+{
+ const char *d;
+ int real_len;
+
+ real_len = _dbus_string_get_length (str);
+
+ _dbus_assert (start >= 0);
+
+ if (start > real_len)
+ {
+ _dbus_verbose (" [%d,%d) is not inside string of length %d\n",
+ start, len, real_len);
+ return;
+ }
+
+ if ((start + len) > real_len)
+ {
+ _dbus_verbose (" [%d,%d) extends outside string of length %d\n",
+ start, len, real_len);
+ len = real_len - start;
+ }
+
+ d = _dbus_string_get_const_data_len (str, start, len);
+
+ _dbus_verbose_bytes ((const unsigned char *) d, len, start);
+}
+
+static int
+map_type_char_to_type (int t)
+{
+ if (t == DBUS_STRUCT_BEGIN_CHAR)
+ return DBUS_TYPE_STRUCT;
+ else if (t == DBUS_DICT_ENTRY_BEGIN_CHAR)
+ return DBUS_TYPE_DICT_ENTRY;
+ else
+ {
+ _dbus_assert (t != DBUS_STRUCT_END_CHAR);
+ _dbus_assert (t != DBUS_DICT_ENTRY_END_CHAR);
+ return t;
+ }
+}
+
+/**
+ * Get the first type in the signature. The difference between this
+ * and just getting the first byte of the signature is that you won't
+ * get DBUS_STRUCT_BEGIN_CHAR, you'll get DBUS_TYPE_STRUCT
+ * instead.
+ *
+ * @param str string containing signature
+ * @param pos where the signature starts
+ * @returns the first type in the signature
+ */
+int
+_dbus_first_type_in_signature (const DBusString *str,
+ int pos)
+{
+ return map_type_char_to_type (_dbus_string_get_byte (str, pos));
+}
+
+/**
+ * Similar to #_dbus_first_type_in_signature, but operates
+ * on a C string buffer.
+ *
+ * @param str a C string buffer
+ * @param pos where the signature starts
+ * @returns the first type in the signature
+ */
+int
+_dbus_first_type_in_signature_c_str (const char *str,
+ int pos)
+{
+ return map_type_char_to_type (str[pos]);
+}
+
+/** @} */
+
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
+#include "dbus-test.h"
+#include <stdio.h>
+
+/**
+ * Reads a block of fixed-length basic values, as an optimization
+ * vs. reading each one individually into a new buffer.
+ *
+ * This function returns the data in-place; it does not make a copy,
+ * and it does not swap the bytes.
+ *
+ * If you ask for #DBUS_TYPE_DOUBLE you will get a "const double*" back
+ * and the "value" argument should be a "const double**" and so on.
+ *
+ * @param str the string to read from
+ * @param pos position to read from
+ * @param element_type type of array elements
+ * @param value place to return the array
+ * @param n_elements number of array elements to read
+ * @param byte_order the byte order, used to read the array length
+ * @param new_pos #NULL or location to store a position after the elements
+ */
+void
+_dbus_marshal_read_fixed_multi (const DBusString *str,
+ int pos,
+ int element_type,
+ const void **value,
+ int n_elements,
+ int byte_order,
+ int *new_pos)
+{
+ int array_len;
+ int alignment;
+
+ _dbus_assert (dbus_type_is_fixed (element_type));
+ _dbus_assert (dbus_type_is_basic (element_type));
+
+#if 0
+ _dbus_verbose ("reading %d elements of %s\n",
+ n_elements, _dbus_type_to_string (element_type));
+#endif
+
+ alignment = _dbus_type_get_alignment (element_type);
+
+ pos = _DBUS_ALIGN_VALUE (pos, alignment);
+
+ array_len = n_elements * alignment;
+
+ *value = _dbus_string_get_const_data_len (str, pos, array_len);
+ if (new_pos)
+ *new_pos = pos + array_len;
+}
+
+static void
+swap_test_array (void *array,
+ int len_bytes,
+ int byte_order,
+ int alignment)
+{
+ DBusString t;
+
+ if (alignment == 1)
+ return;
+
+ _dbus_string_init_const_len (&t, array, len_bytes);
+ swap_array (&t, 0, len_bytes / alignment, byte_order, alignment);
+}
+
+#define MARSHAL_BASIC(typename, byte_order, literal) \
+ do { \
+ v_##typename = literal; \
+ if (!_dbus_marshal_write_basic (&str, pos, DBUS_TYPE_##typename, \
+ &v_##typename, \
+ byte_order, NULL)) \
+ _dbus_test_fatal ("no memory"); \
+ } while (0)
+
+#define DEMARSHAL_BASIC(typename, byte_order) \
+ do { \
+ _dbus_marshal_read_basic (&str, pos, DBUS_TYPE_##typename, &v_##typename, \
+ byte_order, &pos); \
+ } while (0)
+
+#define DEMARSHAL_BASIC_AND_CHECK(typename, byte_order, literal) \
+ do { \
+ DEMARSHAL_BASIC (typename, byte_order); \
+ if (literal != v_##typename) \
+ { \
+ _dbus_verbose_bytes_of_string (&str, dump_pos, \
+ _dbus_string_get_length (&str) - dump_pos); \
+ _dbus_test_fatal ("demarshaled wrong value"); \
+ } \
+ } while (0)
+
+#define MARSHAL_TEST(typename, byte_order, literal) \
+ do { \
+ MARSHAL_BASIC (typename, byte_order, literal); \
+ dump_pos = pos; \
+ DEMARSHAL_BASIC_AND_CHECK (typename, byte_order, literal); \
+ } while (0)
+
+#define MARSHAL_TEST_STRCMP(typename, byte_order, literal) \
+ do { \
+ MARSHAL_BASIC (typename, byte_order, literal); \
+ dump_pos = pos; \
+ DEMARSHAL_BASIC (typename, byte_order); \
+ if (strcmp (literal, v_##typename) != 0) \
+ { \
+ _dbus_verbose_bytes_of_string (&str, dump_pos, \
+ _dbus_string_get_length (&str) - dump_pos); \
+ _dbus_warn ("literal '%s'\nvalue '%s'", literal, v_##typename); \
+ _dbus_test_fatal ("demarshaled wrong value"); \
+ } \
+ } while (0)
+
+#define MARSHAL_FIXED_ARRAY(typename, byte_order, literal) \
+ do { \
+ int next; \
+ v_UINT32 = sizeof(literal); \
+ if (!_dbus_marshal_write_basic (&str, pos, DBUS_TYPE_UINT32, &v_UINT32, \
+ byte_order, &next)) \
+ _dbus_test_fatal ("no memory"); \
+ v_ARRAY_##typename = literal; \
+ if (!_dbus_marshal_write_fixed_multi (&str, next, DBUS_TYPE_##typename, \
+ &v_ARRAY_##typename, _DBUS_N_ELEMENTS(literal), \
+ byte_order, NULL)) \
+ _dbus_test_fatal ("no memory"); \
+ } while (0)
+
+#define DEMARSHAL_FIXED_ARRAY(typename, byte_order) \
+ do { \
+ int next; \
+ alignment = _dbus_type_get_alignment (DBUS_TYPE_##typename); \
+ v_UINT32 = _dbus_marshal_read_uint32 (&str, dump_pos, byte_order, &next); \
+ _dbus_marshal_read_fixed_multi (&str, next, DBUS_TYPE_##typename, \
+ (const void **) &v_ARRAY_##typename, \
+ v_UINT32/alignment, \
+ byte_order, NULL); \
+ swap_test_array (v_ARRAY_##typename, v_UINT32, \
+ byte_order, alignment); \
+ } while (0)
+
+#define DEMARSHAL_FIXED_ARRAY_AND_CHECK(typename, byte_order, literal) \
+ do { \
+ DEMARSHAL_FIXED_ARRAY (typename, byte_order); \
+ if (memcmp (literal, v_ARRAY_##typename, sizeof (literal)) != 0) \
+ { \
+ _dbus_verbose ("MARSHALED DATA\n"); \
+ _dbus_verbose_bytes_of_string (&str, dump_pos, \
+ _dbus_string_get_length (&str) - dump_pos); \
+ _dbus_verbose ("LITERAL DATA\n"); \
+ _dbus_verbose_bytes ((const unsigned char *) literal, sizeof (literal), 0); \
+ _dbus_verbose ("READ DATA\n"); \
+ _dbus_verbose_bytes ((const unsigned char *) v_ARRAY_##typename, sizeof (literal), 0); \
+ _dbus_test_fatal ("demarshaled wrong fixed array value"); \
+ } \
+ } while (0)
+
+#define MARSHAL_TEST_FIXED_ARRAY(typename, byte_order, literal) \
+ do { \
+ MARSHAL_FIXED_ARRAY (typename, byte_order, literal); \
+ dump_pos = pos; \
+ DEMARSHAL_FIXED_ARRAY_AND_CHECK (typename, byte_order, literal); \
+ } while (0)
+
+dbus_bool_t
+_dbus_marshal_test (const char *test_data_dir _DBUS_GNUC_UNUSED)
+{
+ int alignment;
+ DBusString str;
+ int pos, dump_pos;
+ unsigned char array1[5] = { 3, 4, 0, 1, 9 };
+ dbus_int16_t array2[3] = { 124, 457, 780 };
+ dbus_uint16_t array2u[3] = { 124, 457, 780 };
+ dbus_int32_t array4[3] = { 123, 456, 789 };
+ dbus_uint32_t array4u[3] = { 123, 456, 789 };
+ dbus_int64_t array8[3] = { DBUS_INT64_CONSTANT (0x123ffffffff),
+ DBUS_INT64_CONSTANT (0x456ffffffff),
+ DBUS_INT64_CONSTANT (0x789ffffffff) };
+ dbus_int64_t *v_ARRAY_INT64;
+ unsigned char *v_ARRAY_BYTE;
+ dbus_int16_t *v_ARRAY_INT16;
+ dbus_uint16_t *v_ARRAY_UINT16;
+ dbus_int32_t *v_ARRAY_INT32;
+ dbus_uint32_t *v_ARRAY_UINT32;
+ DBusString t;
+ double v_DOUBLE;
+ double t_DOUBLE;
+ dbus_int16_t v_INT16;
+ dbus_uint16_t v_UINT16;
+ dbus_int32_t v_INT32;
+ dbus_uint32_t v_UINT32;
+ dbus_int64_t v_INT64;
+ dbus_uint64_t v_UINT64;
+ unsigned char v_BYTE;
+ dbus_bool_t v_BOOLEAN;
+ const char *v_STRING;
+ const char *v_SIGNATURE;
+ const char *v_OBJECT_PATH;
+ int byte_order;
+
+ if (!_dbus_string_init (&str))
+ _dbus_test_fatal ("failed to init string");
+
+ pos = 0;
+
+ /* Marshal doubles */
+ MARSHAL_BASIC (DOUBLE, DBUS_BIG_ENDIAN, 3.14);
+ DEMARSHAL_BASIC (DOUBLE, DBUS_BIG_ENDIAN);
+ t_DOUBLE = 3.14;
+ if (!_DBUS_DOUBLES_BITWISE_EQUAL (t_DOUBLE, v_DOUBLE))
+ _dbus_test_fatal ("got wrong double value");
+
+ MARSHAL_BASIC (DOUBLE, DBUS_LITTLE_ENDIAN, 3.14);
+ DEMARSHAL_BASIC (DOUBLE, DBUS_LITTLE_ENDIAN);
+ t_DOUBLE = 3.14;
+ if (!_DBUS_DOUBLES_BITWISE_EQUAL (t_DOUBLE, v_DOUBLE))
+ _dbus_test_fatal ("got wrong double value");
+
+ /* Marshal signed 16 integers */
+ MARSHAL_TEST (INT16, DBUS_BIG_ENDIAN, -12345);
+ MARSHAL_TEST (INT16, DBUS_LITTLE_ENDIAN, -12345);
+
+ /* Marshal unsigned 16 integers */
+ MARSHAL_TEST (UINT16, DBUS_BIG_ENDIAN, 0x1234);
+ MARSHAL_TEST (UINT16, DBUS_LITTLE_ENDIAN, 0x1234);
+
+ /* Marshal signed integers */
+ MARSHAL_TEST (INT32, DBUS_BIG_ENDIAN, -12345678);
+ MARSHAL_TEST (INT32, DBUS_LITTLE_ENDIAN, -12345678);
+
+ /* Marshal unsigned integers */
+ MARSHAL_TEST (UINT32, DBUS_BIG_ENDIAN, 0x12345678);
+ MARSHAL_TEST (UINT32, DBUS_LITTLE_ENDIAN, 0x12345678);
+
+ /* Marshal signed integers */
+ MARSHAL_TEST (INT64, DBUS_BIG_ENDIAN, DBUS_INT64_CONSTANT (-0x123456789abc7));
+ MARSHAL_TEST (INT64, DBUS_LITTLE_ENDIAN, DBUS_INT64_CONSTANT (-0x123456789abc7));
+
+ /* Marshal unsigned integers */
+ MARSHAL_TEST (UINT64, DBUS_BIG_ENDIAN, DBUS_UINT64_CONSTANT (0x123456789abc7));
+ MARSHAL_TEST (UINT64, DBUS_LITTLE_ENDIAN, DBUS_UINT64_CONSTANT (0x123456789abc7));
+
+ /* Marshal byte */
+ MARSHAL_TEST (BYTE, DBUS_BIG_ENDIAN, 5);
+ MARSHAL_TEST (BYTE, DBUS_LITTLE_ENDIAN, 5);
+
+ /* Marshal all possible bools! */
+ MARSHAL_TEST (BOOLEAN, DBUS_BIG_ENDIAN, FALSE);
+ MARSHAL_TEST (BOOLEAN, DBUS_LITTLE_ENDIAN, FALSE);
+ MARSHAL_TEST (BOOLEAN, DBUS_BIG_ENDIAN, TRUE);
+ MARSHAL_TEST (BOOLEAN, DBUS_LITTLE_ENDIAN, TRUE);
+
+ /* Marshal strings */
+ MARSHAL_TEST_STRCMP (STRING, DBUS_BIG_ENDIAN, "");
+ MARSHAL_TEST_STRCMP (STRING, DBUS_LITTLE_ENDIAN, "");
+ MARSHAL_TEST_STRCMP (STRING, DBUS_BIG_ENDIAN, "This is the dbus test string");
+ MARSHAL_TEST_STRCMP (STRING, DBUS_LITTLE_ENDIAN, "This is the dbus test string");
+
+ /* object paths */
+ MARSHAL_TEST_STRCMP (OBJECT_PATH, DBUS_BIG_ENDIAN, "/a/b/c");
+ MARSHAL_TEST_STRCMP (OBJECT_PATH, DBUS_LITTLE_ENDIAN, "/a/b/c");
+
+ /* signatures */
+ MARSHAL_TEST_STRCMP (SIGNATURE, DBUS_BIG_ENDIAN, "");
+ MARSHAL_TEST_STRCMP (SIGNATURE, DBUS_LITTLE_ENDIAN, "");
+ MARSHAL_TEST_STRCMP (SIGNATURE, DBUS_BIG_ENDIAN, "a(ii)");
+ MARSHAL_TEST_STRCMP (SIGNATURE, DBUS_LITTLE_ENDIAN, "a(ii)");
+
+ /* Arrays */
+ MARSHAL_TEST_FIXED_ARRAY (INT16, DBUS_BIG_ENDIAN, array2);
+ MARSHAL_TEST_FIXED_ARRAY (INT16, DBUS_LITTLE_ENDIAN, array2);
+ MARSHAL_TEST_FIXED_ARRAY (UINT16, DBUS_BIG_ENDIAN, array2u);
+ MARSHAL_TEST_FIXED_ARRAY (UINT16, DBUS_LITTLE_ENDIAN, array2u);
+
+ MARSHAL_TEST_FIXED_ARRAY (INT32, DBUS_BIG_ENDIAN, array4);
+ MARSHAL_TEST_FIXED_ARRAY (INT32, DBUS_LITTLE_ENDIAN, array4);
+ MARSHAL_TEST_FIXED_ARRAY (UINT32, DBUS_BIG_ENDIAN, array4u);
+ MARSHAL_TEST_FIXED_ARRAY (UINT32, DBUS_LITTLE_ENDIAN, array4u);
+
+ MARSHAL_TEST_FIXED_ARRAY (BYTE, DBUS_BIG_ENDIAN, array1);
+ MARSHAL_TEST_FIXED_ARRAY (BYTE, DBUS_LITTLE_ENDIAN, array1);
+
+ MARSHAL_TEST_FIXED_ARRAY (INT64, DBUS_BIG_ENDIAN, array8);
+ MARSHAL_TEST_FIXED_ARRAY (INT64, DBUS_LITTLE_ENDIAN, array8);
+
+#if 0
+
+ /*
+ * FIXME restore the set/pack tests
+ */
+
+ /* set/pack 64-bit integers */
+ _dbus_string_set_length (&str, 8);
+
+ /* signed little */
+ _dbus_marshal_set_int64 (&str, DBUS_LITTLE_ENDIAN,
+ 0, DBUS_INT64_CONSTANT (-0x123456789abc7));
+
+ _dbus_assert (DBUS_INT64_CONSTANT (-0x123456789abc7) ==
+ _dbus_unpack_int64 (DBUS_LITTLE_ENDIAN,
+ _dbus_string_get_const_data (&str)));
+
+ /* signed big */
+ _dbus_marshal_set_int64 (&str, DBUS_BIG_ENDIAN,
+ 0, DBUS_INT64_CONSTANT (-0x123456789abc7));
+
+ _dbus_assert (DBUS_INT64_CONSTANT (-0x123456789abc7) ==
+ _dbus_unpack_int64 (DBUS_BIG_ENDIAN,
+ _dbus_string_get_const_data (&str)));
+
+ /* signed little pack */
+ _dbus_pack_int64 (DBUS_INT64_CONSTANT (-0x123456789abc7),
+ DBUS_LITTLE_ENDIAN,
+ _dbus_string_get_data (&str));
+
+ _dbus_assert (DBUS_INT64_CONSTANT (-0x123456789abc7) ==
+ _dbus_unpack_int64 (DBUS_LITTLE_ENDIAN,
+ _dbus_string_get_const_data (&str)));
+
+ /* signed big pack */
+ _dbus_pack_int64 (DBUS_INT64_CONSTANT (-0x123456789abc7),
+ DBUS_BIG_ENDIAN,
+ _dbus_string_get_data (&str));
+
+ _dbus_assert (DBUS_INT64_CONSTANT (-0x123456789abc7) ==
+ _dbus_unpack_int64 (DBUS_BIG_ENDIAN,
+ _dbus_string_get_const_data (&str)));
+
+ /* unsigned little */
+ _dbus_marshal_set_uint64 (&str, DBUS_LITTLE_ENDIAN,
+ 0, DBUS_UINT64_CONSTANT (0x123456789abc7));
+
+ _dbus_assert (DBUS_UINT64_CONSTANT (0x123456789abc7) ==
+ _dbus_unpack_uint64 (DBUS_LITTLE_ENDIAN,
+ _dbus_string_get_const_data (&str)));
+
+ /* unsigned big */
+ _dbus_marshal_set_uint64 (&str, DBUS_BIG_ENDIAN,
+ 0, DBUS_UINT64_CONSTANT (0x123456789abc7));
+
+ _dbus_assert (DBUS_UINT64_CONSTANT (0x123456789abc7) ==
+ _dbus_unpack_uint64 (DBUS_BIG_ENDIAN,
+ _dbus_string_get_const_data (&str)));
+
+ /* unsigned little pack */
+ _dbus_pack_uint64 (DBUS_UINT64_CONSTANT (0x123456789abc7),
+ DBUS_LITTLE_ENDIAN,
+ _dbus_string_get_data (&str));
+
+ _dbus_assert (DBUS_UINT64_CONSTANT (0x123456789abc7) ==
+ _dbus_unpack_uint64 (DBUS_LITTLE_ENDIAN,
+ _dbus_string_get_const_data (&str)));
+
+ /* unsigned big pack */
+ _dbus_pack_uint64 (DBUS_UINT64_CONSTANT (0x123456789abc7),
+ DBUS_BIG_ENDIAN,
+ _dbus_string_get_data (&str));
+
+ _dbus_assert (DBUS_UINT64_CONSTANT (0x123456789abc7) ==
+ _dbus_unpack_uint64 (DBUS_BIG_ENDIAN,
+ _dbus_string_get_const_data (&str)));
+
+ /* set/pack 32-bit integers */
+ _dbus_string_set_length (&str, 4);
+
+ /* signed little */
+ _dbus_marshal_set_int32 (&str, DBUS_LITTLE_ENDIAN,
+ 0, -0x123456);
+
+ _dbus_assert (-0x123456 ==
+ _dbus_unpack_int32 (DBUS_LITTLE_ENDIAN,
+ _dbus_string_get_const_data (&str)));
+
+ /* signed big */
+ _dbus_marshal_set_int32 (&str, DBUS_BIG_ENDIAN,
+ 0, -0x123456);
+
+ _dbus_assert (-0x123456 ==
+ _dbus_unpack_int32 (DBUS_BIG_ENDIAN,
+ _dbus_string_get_const_data (&str)));
+
+ /* signed little pack */
+ _dbus_pack_int32 (-0x123456,
+ DBUS_LITTLE_ENDIAN,
+ _dbus_string_get_data (&str));
+
+ _dbus_assert (-0x123456 ==
+ _dbus_unpack_int32 (DBUS_LITTLE_ENDIAN,
+ _dbus_string_get_const_data (&str)));
+
+ /* signed big pack */
+ _dbus_pack_int32 (-0x123456,
+ DBUS_BIG_ENDIAN,
+ _dbus_string_get_data (&str));
+
+ _dbus_assert (-0x123456 ==
+ _dbus_unpack_int32 (DBUS_BIG_ENDIAN,
+ _dbus_string_get_const_data (&str)));
+
+ /* unsigned little */
+ _dbus_marshal_set_uint32 (&str,
+ 0, 0x123456,
+ DBUS_LITTLE_ENDIAN);
+
+ _dbus_assert (0x123456 ==
+ _dbus_unpack_uint32 (DBUS_LITTLE_ENDIAN,
+ _dbus_string_get_const_data (&str)));
+
+ /* unsigned big */
+ _dbus_marshal_set_uint32 (&str,
+ 0, 0x123456,
+ DBUS_BIG_ENDIAN);
+
+ _dbus_assert (0x123456 ==
+ _dbus_unpack_uint32 (DBUS_BIG_ENDIAN,
+ _dbus_string_get_const_data (&str)));
+
+ /* unsigned little pack */
+ _dbus_pack_uint32 (0x123456,
+ DBUS_LITTLE_ENDIAN,
+ _dbus_string_get_data (&str));
+
+ _dbus_assert (0x123456 ==
+ _dbus_unpack_uint32 (DBUS_LITTLE_ENDIAN,
+ _dbus_string_get_const_data (&str)));
+
+ /* unsigned big pack */
+ _dbus_pack_uint32 (0x123456,
+ DBUS_BIG_ENDIAN,
+ _dbus_string_get_data (&str));
+
+ _dbus_assert (0x123456 ==
+ _dbus_unpack_uint32 (DBUS_BIG_ENDIAN,
+ _dbus_string_get_const_data (&str)));
+
+#endif /* set/pack tests for integers */
+
+ /* Strings in-place set */
+ byte_order = DBUS_LITTLE_ENDIAN;
+ while (TRUE)
+ {
+ /* Init a string */
+ _dbus_string_set_length (&str, 0);
+
+ /* reset pos for the macros */
+ pos = 0;
+
+ MARSHAL_TEST_STRCMP (STRING, byte_order, "Hello world");
+
+ /* Set it to something longer */
+ _dbus_string_init_const (&t, "Hello world foo");
+
+ v_STRING = _dbus_string_get_const_data (&t);
+ _dbus_marshal_set_basic (&str, 0, DBUS_TYPE_STRING,
+ &v_STRING, byte_order, NULL, NULL);
+
+ _dbus_marshal_read_basic (&str, 0, DBUS_TYPE_STRING,
+ &v_STRING, byte_order,
+ NULL);
+ _dbus_assert (strcmp (v_STRING, "Hello world foo") == 0);
+
+ /* Set it to something shorter */
+ _dbus_string_init_const (&t, "Hello");
+
+ v_STRING = _dbus_string_get_const_data (&t);
+ _dbus_marshal_set_basic (&str, 0, DBUS_TYPE_STRING,
+ &v_STRING, byte_order, NULL, NULL);
+ _dbus_marshal_read_basic (&str, 0, DBUS_TYPE_STRING,
+ &v_STRING, byte_order,
+ NULL);
+ _dbus_assert (strcmp (v_STRING, "Hello") == 0);
+
+ /* Do the other byte order */
+ if (byte_order == DBUS_LITTLE_ENDIAN)
+ byte_order = DBUS_BIG_ENDIAN;
+ else
+ break;
+ }
+
+ /* Clean up */
+ _dbus_string_free (&str);
+
+ return TRUE;
+}
+
+#endif /* DBUS_ENABLE_EMBEDDED_TESTS */
diff --git a/src/3rdparty/libdbus/dbus/dbus-marshal-basic.h b/src/3rdparty/libdbus/dbus/dbus-marshal-basic.h
new file mode 100644
index 00000000..48b90d6a
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-marshal-basic.h
@@ -0,0 +1,233 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-marshal-basic.h Marshalling routines for basic (primitive) types
+ *
+ * Copyright (C) 2002 CodeFactory AB
+ * Copyright (C) 2004, 2005 Red Hat, Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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
+ *
+ */
+
+#ifndef DBUS_MARSHAL_BASIC_H
+#define DBUS_MARSHAL_BASIC_H
+
+#ifdef HAVE_BYTESWAP_H
+#include <byteswap.h>
+#endif
+
+#include <dbus/dbus-protocol.h>
+#include <dbus/dbus-types.h>
+#include <dbus/dbus-arch-deps.h>
+#include <dbus/dbus-string.h>
+
+#ifdef WORDS_BIGENDIAN
+#define DBUS_COMPILER_BYTE_ORDER DBUS_BIG_ENDIAN
+#else
+#define DBUS_COMPILER_BYTE_ORDER DBUS_LITTLE_ENDIAN
+#endif
+
+#ifdef HAVE_BYTESWAP_H
+#define DBUS_UINT16_SWAP_LE_BE_CONSTANT(val) bswap_16(val)
+#define DBUS_UINT32_SWAP_LE_BE_CONSTANT(val) bswap_32(val)
+#else /* HAVE_BYTESWAP_H */
+
+#define DBUS_UINT16_SWAP_LE_BE_CONSTANT(val) ((dbus_uint16_t) ( \
+ (dbus_uint16_t) ((dbus_uint16_t) (val) >> 8) | \
+ (dbus_uint16_t) ((dbus_uint16_t) (val) << 8)))
+
+#define DBUS_UINT32_SWAP_LE_BE_CONSTANT(val) ((dbus_uint32_t) ( \
+ (((dbus_uint32_t) (val) & (dbus_uint32_t) 0x000000ffU) << 24) | \
+ (((dbus_uint32_t) (val) & (dbus_uint32_t) 0x0000ff00U) << 8) | \
+ (((dbus_uint32_t) (val) & (dbus_uint32_t) 0x00ff0000U) >> 8) | \
+ (((dbus_uint32_t) (val) & (dbus_uint32_t) 0xff000000U) >> 24)))
+
+#endif /* HAVE_BYTESWAP_H */
+
+#ifdef HAVE_BYTESWAP_H
+#define DBUS_UINT64_SWAP_LE_BE_CONSTANT(val) bswap_64(val)
+#else /* HAVE_BYTESWAP_H */
+
+#define DBUS_UINT64_SWAP_LE_BE_CONSTANT(val) ((dbus_uint64_t) ( \
+ (((dbus_uint64_t) (val) & \
+ (dbus_uint64_t) DBUS_UINT64_CONSTANT (0x00000000000000ff)) << 56) | \
+ (((dbus_uint64_t) (val) & \
+ (dbus_uint64_t) DBUS_UINT64_CONSTANT (0x000000000000ff00)) << 40) | \
+ (((dbus_uint64_t) (val) & \
+ (dbus_uint64_t) DBUS_UINT64_CONSTANT (0x0000000000ff0000)) << 24) | \
+ (((dbus_uint64_t) (val) & \
+ (dbus_uint64_t) DBUS_UINT64_CONSTANT (0x00000000ff000000)) << 8) | \
+ (((dbus_uint64_t) (val) & \
+ (dbus_uint64_t) DBUS_UINT64_CONSTANT (0x000000ff00000000)) >> 8) | \
+ (((dbus_uint64_t) (val) & \
+ (dbus_uint64_t) DBUS_UINT64_CONSTANT (0x0000ff0000000000)) >> 24) | \
+ (((dbus_uint64_t) (val) & \
+ (dbus_uint64_t) DBUS_UINT64_CONSTANT (0x00ff000000000000)) >> 40) | \
+ (((dbus_uint64_t) (val) & \
+ (dbus_uint64_t) DBUS_UINT64_CONSTANT (0xff00000000000000)) >> 56)))
+
+#endif /* HAVE_BYTESWAP_H */
+
+#define DBUS_UINT16_SWAP_LE_BE(val) (DBUS_UINT16_SWAP_LE_BE_CONSTANT (val))
+#define DBUS_INT16_SWAP_LE_BE(val) ((dbus_int16_t)DBUS_UINT16_SWAP_LE_BE_CONSTANT (val))
+
+#define DBUS_UINT32_SWAP_LE_BE(val) (DBUS_UINT32_SWAP_LE_BE_CONSTANT (val))
+#define DBUS_INT32_SWAP_LE_BE(val) ((dbus_int32_t)DBUS_UINT32_SWAP_LE_BE_CONSTANT (val))
+
+#define DBUS_UINT64_SWAP_LE_BE(val) (DBUS_UINT64_SWAP_LE_BE_CONSTANT (val))
+#define DBUS_INT64_SWAP_LE_BE(val) ((dbus_int64_t)DBUS_UINT64_SWAP_LE_BE_CONSTANT (val))
+
+#ifdef WORDS_BIGENDIAN
+
+# define DBUS_INT16_TO_BE(val) ((dbus_int16_t) (val))
+# define DBUS_UINT16_TO_BE(val) ((dbus_uint16_t) (val))
+# define DBUS_INT16_TO_LE(val) (DBUS_INT16_SWAP_LE_BE (val))
+# define DBUS_UINT16_TO_LE(val) (DBUS_UINT16_SWAP_LE_BE (val))
+# define DBUS_INT32_TO_BE(val) ((dbus_int32_t) (val))
+# define DBUS_UINT32_TO_BE(val) ((dbus_uint32_t) (val))
+# define DBUS_INT32_TO_LE(val) (DBUS_INT32_SWAP_LE_BE (val))
+# define DBUS_UINT32_TO_LE(val) (DBUS_UINT32_SWAP_LE_BE (val))
+# define DBUS_INT64_TO_BE(val) ((dbus_int64_t) (val))
+# define DBUS_UINT64_TO_BE(val) ((dbus_uint64_t) (val))
+# define DBUS_INT64_TO_LE(val) (DBUS_INT64_SWAP_LE_BE (val))
+# define DBUS_UINT64_TO_LE(val) (DBUS_UINT64_SWAP_LE_BE (val))
+
+#else /* WORDS_BIGENDIAN */
+
+# define DBUS_INT16_TO_LE(val) ((dbus_int16_t) (val))
+# define DBUS_UINT16_TO_LE(val) ((dbus_uint16_t) (val))
+# define DBUS_INT16_TO_BE(val) ((dbus_int16_t) DBUS_UINT16_SWAP_LE_BE (val))
+# define DBUS_UINT16_TO_BE(val) (DBUS_UINT16_SWAP_LE_BE (val))
+# define DBUS_INT32_TO_LE(val) ((dbus_int32_t) (val))
+# define DBUS_UINT32_TO_LE(val) ((dbus_uint32_t) (val))
+# define DBUS_INT32_TO_BE(val) ((dbus_int32_t) DBUS_UINT32_SWAP_LE_BE (val))
+# define DBUS_UINT32_TO_BE(val) (DBUS_UINT32_SWAP_LE_BE (val))
+# define DBUS_INT64_TO_LE(val) ((dbus_int64_t) (val))
+# define DBUS_UINT64_TO_LE(val) ((dbus_uint64_t) (val))
+# define DBUS_INT64_TO_BE(val) ((dbus_int64_t) DBUS_UINT64_SWAP_LE_BE (val))
+# define DBUS_UINT64_TO_BE(val) (DBUS_UINT64_SWAP_LE_BE (val))
+#endif
+
+/* The transformation is symmetric, so the FROM just maps to the TO. */
+#define DBUS_INT16_FROM_LE(val) (DBUS_INT16_TO_LE (val))
+#define DBUS_UINT16_FROM_LE(val) (DBUS_UINT16_TO_LE (val))
+#define DBUS_INT16_FROM_BE(val) (DBUS_INT16_TO_BE (val))
+#define DBUS_UINT16_FROM_BE(val) (DBUS_UINT16_TO_BE (val))
+#define DBUS_INT32_FROM_LE(val) (DBUS_INT32_TO_LE (val))
+#define DBUS_UINT32_FROM_LE(val) (DBUS_UINT32_TO_LE (val))
+#define DBUS_INT32_FROM_BE(val) (DBUS_INT32_TO_BE (val))
+#define DBUS_UINT32_FROM_BE(val) (DBUS_UINT32_TO_BE (val))
+#define DBUS_INT64_FROM_LE(val) (DBUS_INT64_TO_LE (val))
+#define DBUS_UINT64_FROM_LE(val) (DBUS_UINT64_TO_LE (val))
+#define DBUS_INT64_FROM_BE(val) (DBUS_INT64_TO_BE (val))
+#define DBUS_UINT64_FROM_BE(val) (DBUS_UINT64_TO_BE (val))
+
+#ifdef DBUS_DISABLE_ASSERT
+#define _dbus_unpack_uint16(byte_order, data) \
+ (((byte_order) == DBUS_LITTLE_ENDIAN) ? \
+ DBUS_UINT16_FROM_LE (*(dbus_uint16_t*)(data)) : \
+ DBUS_UINT16_FROM_BE (*(dbus_uint16_t*)(data)))
+
+#define _dbus_unpack_uint32(byte_order, data) \
+ (((byte_order) == DBUS_LITTLE_ENDIAN) ? \
+ DBUS_UINT32_FROM_LE (*(dbus_uint32_t*)(data)) : \
+ DBUS_UINT32_FROM_BE (*(dbus_uint32_t*)(data)))
+#endif
+
+#ifndef _dbus_unpack_uint16
+DBUS_PRIVATE_EXPORT
+dbus_uint16_t _dbus_unpack_uint16 (int byte_order,
+ const unsigned char *data);
+#endif
+
+void _dbus_pack_uint32 (dbus_uint32_t value,
+ int byte_order,
+ unsigned char *data);
+#ifndef _dbus_unpack_uint32
+DBUS_PRIVATE_EXPORT
+dbus_uint32_t _dbus_unpack_uint32 (int byte_order,
+ const unsigned char *data);
+#endif
+
+dbus_bool_t _dbus_marshal_set_basic (DBusString *str,
+ int pos,
+ int type,
+ const void *value,
+ int byte_order,
+ int *old_end_pos,
+ int *new_end_pos);
+dbus_bool_t _dbus_marshal_write_basic (DBusString *str,
+ int insert_at,
+ int type,
+ const void *value,
+ int byte_order,
+ int *pos_after);
+dbus_bool_t _dbus_marshal_write_fixed_multi (DBusString *str,
+ int insert_at,
+ int element_type,
+ const void *value,
+ int n_elements,
+ int byte_order,
+ int *pos_after);
+void _dbus_marshal_read_basic (const DBusString *str,
+ int pos,
+ int type,
+ void *value,
+ int byte_order,
+ int *new_pos);
+void _dbus_marshal_read_fixed_multi (const DBusString *str,
+ int pos,
+ int element_type,
+ const void **value,
+ int n_elements,
+ int byte_order,
+ int *new_pos);
+void _dbus_marshal_skip_basic (const DBusString *str,
+ int type,
+ int byte_order,
+ int *pos);
+void _dbus_marshal_skip_array (const DBusString *str,
+ int element_type,
+ int byte_order,
+ int *pos);
+DBUS_PRIVATE_EXPORT
+void _dbus_marshal_set_uint32 (DBusString *str,
+ int pos,
+ dbus_uint32_t value,
+ int byte_order);
+DBUS_PRIVATE_EXPORT
+dbus_uint32_t _dbus_marshal_read_uint32 (const DBusString *str,
+ int pos,
+ int byte_order,
+ int *new_pos);
+int _dbus_type_get_alignment (int typecode);
+DBUS_PRIVATE_EXPORT
+const char* _dbus_type_to_string (int typecode);
+
+DBUS_PRIVATE_EXPORT
+int _dbus_first_type_in_signature (const DBusString *str,
+ int pos);
+
+int _dbus_first_type_in_signature_c_str (const char *str,
+ int pos);
+
+void _dbus_swap_array (unsigned char *data,
+ int n_elements,
+ int alignment);
+
+#endif /* DBUS_MARSHAL_BASIC_H */
diff --git a/src/3rdparty/libdbus/dbus/dbus-marshal-byteswap.c b/src/3rdparty/libdbus/dbus/dbus-marshal-byteswap.c
new file mode 100644
index 00000000..1aea5d07
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-marshal-byteswap.c
@@ -0,0 +1,250 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-marshal-byteswap.c Swap a block of marshaled data
+ *
+ * Copyright (C) 2005 Red Hat, Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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-marshal-byteswap.h"
+#include "dbus-marshal-basic.h"
+#include "dbus-signature.h"
+
+/**
+ * @addtogroup DBusMarshal
+ * @{
+ */
+
+static void
+byteswap_body_helper (DBusTypeReader *reader,
+ dbus_bool_t walk_reader_to_end,
+ int old_byte_order,
+ int new_byte_order,
+ unsigned char *p,
+ unsigned char **new_p)
+{
+ int current_type;
+
+ while ((current_type = _dbus_type_reader_get_current_type (reader)) != DBUS_TYPE_INVALID)
+ {
+ switch (current_type)
+ {
+ case DBUS_TYPE_BYTE:
+ ++p;
+ break;
+
+ case DBUS_TYPE_INT16:
+ case DBUS_TYPE_UINT16:
+ {
+ p = _DBUS_ALIGN_ADDRESS (p, 2);
+ *((dbus_uint16_t *) (void *) p) =
+ DBUS_UINT16_SWAP_LE_BE (*((dbus_uint16_t *) (void *) p));
+ p += 2;
+ }
+ break;
+
+ case DBUS_TYPE_BOOLEAN:
+ case DBUS_TYPE_INT32:
+ case DBUS_TYPE_UINT32:
+ case DBUS_TYPE_UNIX_FD:
+ {
+ p = _DBUS_ALIGN_ADDRESS (p, 4);
+ *((dbus_uint32_t *) (void *) p) =
+ DBUS_UINT32_SWAP_LE_BE (*((dbus_uint32_t *) (void *) p));
+ p += 4;
+ }
+ break;
+
+ case DBUS_TYPE_INT64:
+ case DBUS_TYPE_UINT64:
+ case DBUS_TYPE_DOUBLE:
+ {
+ p = _DBUS_ALIGN_ADDRESS (p, 8);
+ *((dbus_uint64_t *) (void *) p) =
+ DBUS_UINT64_SWAP_LE_BE (*((dbus_uint64_t *) (void *) p));
+ p += 8;
+ }
+ break;
+
+ case DBUS_TYPE_ARRAY:
+ case DBUS_TYPE_STRING:
+ case DBUS_TYPE_OBJECT_PATH:
+ {
+ dbus_uint32_t array_len;
+
+ p = _DBUS_ALIGN_ADDRESS (p, 4);
+
+ array_len = _dbus_unpack_uint32 (old_byte_order, p);
+
+ *((dbus_uint32_t *) (void *) p) =
+ DBUS_UINT32_SWAP_LE_BE (*((dbus_uint32_t *) (void *) p));
+ p += 4;
+
+ if (current_type == DBUS_TYPE_ARRAY)
+ {
+ int elem_type;
+ int alignment;
+
+ elem_type = _dbus_type_reader_get_element_type (reader);
+ alignment = _dbus_type_get_alignment (elem_type);
+
+ _dbus_assert ((array_len / alignment) < DBUS_MAXIMUM_ARRAY_LENGTH);
+
+ p = _DBUS_ALIGN_ADDRESS (p, alignment);
+
+ if (dbus_type_is_fixed (elem_type))
+ {
+ if (alignment > 1)
+ _dbus_swap_array (p, array_len / alignment, alignment);
+ p += array_len;
+ }
+ else
+ {
+ DBusTypeReader sub;
+ const unsigned char *array_end;
+
+ array_end = p + array_len;
+
+ _dbus_type_reader_recurse (reader, &sub);
+
+ while (p < array_end)
+ {
+ byteswap_body_helper (&sub,
+ FALSE,
+ old_byte_order,
+ new_byte_order,
+ p, &p);
+ }
+ }
+ }
+ else
+ {
+ _dbus_assert (current_type == DBUS_TYPE_STRING ||
+ current_type == DBUS_TYPE_OBJECT_PATH);
+
+ p += (array_len + 1); /* + 1 for nul */
+ }
+ }
+ break;
+
+ case DBUS_TYPE_SIGNATURE:
+ {
+ dbus_uint32_t sig_len;
+
+ sig_len = *p;
+
+ p += (sig_len + 2); /* +2 for len and nul */
+ }
+ break;
+
+ case DBUS_TYPE_VARIANT:
+ {
+ /* 1 byte sig len, sig typecodes, align to
+ * contained-type-boundary, values.
+ */
+ dbus_uint32_t sig_len;
+ DBusString sig;
+ DBusTypeReader sub;
+ int contained_alignment;
+
+ sig_len = *p;
+ ++p;
+
+ _dbus_string_init_const_len (&sig, (const char *) p, sig_len);
+
+ p += (sig_len + 1); /* 1 for nul */
+
+ contained_alignment = _dbus_type_get_alignment (_dbus_first_type_in_signature (&sig, 0));
+
+ p = _DBUS_ALIGN_ADDRESS (p, contained_alignment);
+
+ _dbus_type_reader_init_types_only (&sub, &sig, 0);
+
+ byteswap_body_helper (&sub, FALSE, old_byte_order, new_byte_order, p, &p);
+ }
+ break;
+
+ case DBUS_TYPE_STRUCT:
+ case DBUS_TYPE_DICT_ENTRY:
+ {
+ DBusTypeReader sub;
+
+ p = _DBUS_ALIGN_ADDRESS (p, 8);
+
+ _dbus_type_reader_recurse (reader, &sub);
+
+ byteswap_body_helper (&sub, TRUE, old_byte_order, new_byte_order, p, &p);
+ }
+ break;
+
+ default:
+ _dbus_assert_not_reached ("invalid typecode in supposedly-validated signature");
+ break;
+ }
+
+ if (walk_reader_to_end)
+ _dbus_type_reader_next (reader);
+ else
+ break;
+ }
+
+ if (new_p)
+ *new_p = p;
+}
+
+/**
+ * Byteswaps the marshaled data in the given value_str.
+ *
+ * @param signature the types in the value_str
+ * @param signature_start where in signature is the signature
+ * @param old_byte_order the old byte order
+ * @param new_byte_order the new byte order
+ * @param value_str the string containing the body
+ * @param value_pos where the values start
+ */
+void
+_dbus_marshal_byteswap (const DBusString *signature,
+ int signature_start,
+ int old_byte_order,
+ int new_byte_order,
+ DBusString *value_str,
+ int value_pos)
+{
+ DBusTypeReader reader;
+
+ _dbus_assert (value_pos >= 0);
+ _dbus_assert (value_pos <= _dbus_string_get_length (value_str));
+
+ if (old_byte_order == new_byte_order)
+ return;
+
+ _dbus_type_reader_init_types_only (&reader,
+ signature, signature_start);
+
+ byteswap_body_helper (&reader, TRUE,
+ old_byte_order, new_byte_order,
+ _dbus_string_get_udata_len (value_str, value_pos, 0),
+ NULL);
+}
+
+/** @} */
+
+/* Tests in dbus-marshal-byteswap-util.c */
diff --git a/src/3rdparty/libdbus/dbus/dbus-marshal-byteswap.h b/src/3rdparty/libdbus/dbus/dbus-marshal-byteswap.h
new file mode 100644
index 00000000..b64ba385
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-marshal-byteswap.h
@@ -0,0 +1,40 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-marshal-byteswap.h Swap a block of marshaled data
+ *
+ * Copyright (C) 2005 Red Hat, Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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
+ *
+ */
+
+#ifndef DBUS_MARSHAL_BYTESWAP_H
+#define DBUS_MARSHAL_BYTESWAP_H
+
+#include <dbus/dbus-protocol.h>
+#include <dbus/dbus-marshal-recursive.h>
+
+DBUS_PRIVATE_EXPORT
+void _dbus_marshal_byteswap (const DBusString *signature,
+ int signature_start,
+ int old_byte_order,
+ int new_byte_order,
+ DBusString *value_str,
+ int value_pos);
+
+#endif /* DBUS_MARSHAL_BYTESWAP_H */
diff --git a/src/3rdparty/libdbus/dbus/dbus-marshal-header.c b/src/3rdparty/libdbus/dbus/dbus-marshal-header.c
new file mode 100644
index 00000000..30636d79
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-marshal-header.c
@@ -0,0 +1,1576 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-marshal-header.c Managing marshaling/demarshaling of message headers
+ *
+ * Copyright (C) 2005 Red Hat, Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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/dbus-shared.h"
+#include "dbus-marshal-header.h"
+#include "dbus-marshal-recursive.h"
+#include "dbus-marshal-byteswap.h"
+
+/**
+ * @addtogroup DBusMarshal
+ *
+ * @{
+ */
+
+
+/* Not thread locked, but strictly const/read-only so should be OK
+ */
+/** Static #DBusString containing the signature of a message header */
+_DBUS_STRING_DEFINE_STATIC(_dbus_header_signature_str, DBUS_HEADER_SIGNATURE);
+/** Static #DBusString containing the local interface */
+_DBUS_STRING_DEFINE_STATIC(_dbus_local_interface_str, DBUS_INTERFACE_LOCAL);
+/** Static #DBusString containing the local path */
+_DBUS_STRING_DEFINE_STATIC(_dbus_local_path_str, DBUS_PATH_LOCAL);
+
+/** Offset from start of _dbus_header_signature_str to the signature of the fields array */
+#define FIELDS_ARRAY_SIGNATURE_OFFSET 6
+/** Offset from start of _dbus_header_signature_str to the signature of an element of the fields array */
+#define FIELDS_ARRAY_ELEMENT_SIGNATURE_OFFSET 7
+
+
+/** Offset to byte order from start of header */
+#define BYTE_ORDER_OFFSET 0
+/** Offset to type from start of header */
+#define TYPE_OFFSET 1
+/** Offset to flags from start of header */
+#define FLAGS_OFFSET 2
+/** Offset to version from start of header */
+#define VERSION_OFFSET 3
+/** Offset to body length from start of header */
+#define BODY_LENGTH_OFFSET 4
+/** Offset to client serial from start of header */
+#define SERIAL_OFFSET 8
+/** Offset to fields array length from start of header */
+#define FIELDS_ARRAY_LENGTH_OFFSET 12
+/** Offset to first field in header */
+#define FIRST_FIELD_OFFSET 16
+
+typedef struct
+{
+ unsigned char code; /**< the field code */
+ unsigned char type; /**< the value type */
+} HeaderFieldType;
+
+static const HeaderFieldType
+_dbus_header_field_types[DBUS_HEADER_FIELD_LAST+1] = {
+ { DBUS_HEADER_FIELD_INVALID, DBUS_TYPE_INVALID },
+ { DBUS_HEADER_FIELD_PATH, DBUS_TYPE_OBJECT_PATH },
+ { DBUS_HEADER_FIELD_INTERFACE, DBUS_TYPE_STRING },
+ { DBUS_HEADER_FIELD_MEMBER, DBUS_TYPE_STRING },
+ { DBUS_HEADER_FIELD_ERROR_NAME, DBUS_TYPE_STRING },
+ { DBUS_HEADER_FIELD_REPLY_SERIAL, DBUS_TYPE_UINT32 },
+ { DBUS_HEADER_FIELD_DESTINATION, DBUS_TYPE_STRING },
+ { DBUS_HEADER_FIELD_SENDER, DBUS_TYPE_STRING },
+ { DBUS_HEADER_FIELD_SIGNATURE, DBUS_TYPE_SIGNATURE },
+ { DBUS_HEADER_FIELD_UNIX_FDS, DBUS_TYPE_UINT32 },
+ { DBUS_HEADER_FIELD_CONTAINER_INSTANCE, DBUS_TYPE_OBJECT_PATH }
+};
+
+/** Macro to look up the correct type for a field */
+#define EXPECTED_TYPE_OF_FIELD(field) (_dbus_header_field_types[field].type)
+
+/** The most padding we could ever need for a header */
+#define MAX_POSSIBLE_HEADER_PADDING 7
+static dbus_bool_t
+reserve_header_padding (DBusHeader *header)
+{
+ _dbus_assert (header->padding <= MAX_POSSIBLE_HEADER_PADDING);
+
+ if (!_dbus_string_lengthen (&header->data,
+ MAX_POSSIBLE_HEADER_PADDING - header->padding))
+ return FALSE;
+ header->padding = MAX_POSSIBLE_HEADER_PADDING;
+ return TRUE;
+}
+
+static void
+correct_header_padding (DBusHeader *header)
+{
+ int unpadded_len;
+
+ _dbus_assert (header->padding == 7);
+
+ _dbus_string_shorten (&header->data, header->padding);
+ unpadded_len = _dbus_string_get_length (&header->data);
+
+ if (!_dbus_string_align_length (&header->data, 8))
+ _dbus_assert_not_reached ("couldn't pad header though enough padding was preallocated");
+
+ header->padding = _dbus_string_get_length (&header->data) - unpadded_len;
+}
+
+/**
+ * Compute the end of the header, ignoring padding.
+ * In the #DBusHeader diagram, this is the distance from 0 to [B]. */
+#define HEADER_END_BEFORE_PADDING(header) \
+ (_dbus_string_get_length (&(header)->data) - (header)->padding)
+
+/**
+ * Invalidates all fields in the cache. This may be used when the
+ * cache is totally uninitialized (contains junk) so should not
+ * look at what's in there now.
+ *
+ * @param header the header
+ */
+static void
+_dbus_header_cache_invalidate_all (DBusHeader *header)
+{
+ int i;
+
+ i = 0;
+ while (i <= DBUS_HEADER_FIELD_LAST)
+ {
+ header->fields[i].value_pos = _DBUS_HEADER_FIELD_VALUE_UNKNOWN;
+ ++i;
+ }
+}
+
+/**
+ * Caches one field
+ *
+ * @param header the header
+ * @param field_code the field
+ * @param variant_reader the reader for the variant in the field
+ */
+static void
+_dbus_header_cache_one (DBusHeader *header,
+ int field_code,
+ DBusTypeReader *variant_reader)
+{
+ header->fields[field_code].value_pos =
+ _dbus_type_reader_get_value_pos (variant_reader);
+
+#if 0
+ _dbus_verbose ("cached value_pos %d for field %d\n",
+ header->fields[field_code].value_pos, field_code)
+#endif
+}
+
+/**
+ * Returns the header's byte order.
+ *
+ * @param header the header
+ * @returns the byte order
+ */
+char
+_dbus_header_get_byte_order (const DBusHeader *header)
+{
+ _dbus_assert (_dbus_string_get_length (&header->data) > BYTE_ORDER_OFFSET);
+
+ return (char) _dbus_string_get_byte (&header->data, BYTE_ORDER_OFFSET);
+}
+
+/**
+ * Revalidates the fields cache
+ *
+ * @param header the header
+ */
+static void
+_dbus_header_cache_revalidate (DBusHeader *header)
+{
+ DBusTypeReader array;
+ DBusTypeReader reader;
+ int i;
+
+ i = 0;
+ while (i <= DBUS_HEADER_FIELD_LAST)
+ {
+ header->fields[i].value_pos = _DBUS_HEADER_FIELD_VALUE_NONEXISTENT;
+ ++i;
+ }
+
+ _dbus_type_reader_init (&reader,
+ _dbus_header_get_byte_order (header),
+ &_dbus_header_signature_str,
+ FIELDS_ARRAY_SIGNATURE_OFFSET,
+ &header->data,
+ FIELDS_ARRAY_LENGTH_OFFSET);
+
+ _dbus_type_reader_recurse (&reader, &array);
+
+ while (_dbus_type_reader_get_current_type (&array) != DBUS_TYPE_INVALID)
+ {
+ DBusTypeReader sub;
+ DBusTypeReader variant;
+ unsigned char field_code;
+
+ _dbus_type_reader_recurse (&array, &sub);
+
+ _dbus_assert (_dbus_type_reader_get_current_type (&sub) == DBUS_TYPE_BYTE);
+ _dbus_type_reader_read_basic (&sub, &field_code);
+
+ /* Unknown fields should be ignored */
+ if (field_code > DBUS_HEADER_FIELD_LAST)
+ goto next_field;
+
+ _dbus_type_reader_next (&sub);
+
+ _dbus_assert (_dbus_type_reader_get_current_type (&sub) == DBUS_TYPE_VARIANT);
+ _dbus_type_reader_recurse (&sub, &variant);
+
+ _dbus_header_cache_one (header, field_code, &variant);
+
+ next_field:
+ _dbus_type_reader_next (&array);
+ }
+}
+
+/**
+ * Checks for a field, updating the cache if required.
+ *
+ * @param header the header
+ * @param field the field to check
+ * @returns #FALSE if the field doesn't exist
+ */
+static dbus_bool_t
+_dbus_header_cache_check (DBusHeader *header,
+ int field)
+{
+ _dbus_assert (field <= DBUS_HEADER_FIELD_LAST);
+
+ if (header->fields[field].value_pos == _DBUS_HEADER_FIELD_VALUE_UNKNOWN)
+ _dbus_header_cache_revalidate (header);
+
+ if (header->fields[field].value_pos == _DBUS_HEADER_FIELD_VALUE_NONEXISTENT)
+ return FALSE;
+
+ return TRUE;
+}
+
+/**
+ * Checks whether a field is known not to exist. It may exist
+ * even if it's not known to exist.
+ *
+ * @param header the header
+ * @param field the field to check
+ * @returns #FALSE if the field definitely doesn't exist
+ */
+static dbus_bool_t
+_dbus_header_cache_known_nonexistent (DBusHeader *header,
+ int field)
+{
+ _dbus_assert (field <= DBUS_HEADER_FIELD_LAST);
+
+ return (header->fields[field].value_pos == _DBUS_HEADER_FIELD_VALUE_NONEXISTENT);
+}
+
+/**
+ * Writes a struct of { byte, variant } with the given basic type.
+ *
+ * @param writer the writer (should be ready to write a struct)
+ * @param field the header field
+ * @param type the type of the value
+ * @param value the value as for _dbus_marshal_set_basic()
+ * @returns #FALSE if no memory
+ */
+static dbus_bool_t
+write_basic_field (DBusTypeWriter *writer,
+ int field,
+ int type,
+ const void *value)
+{
+ DBusTypeWriter sub;
+ DBusTypeWriter variant;
+ int start;
+ int padding;
+ unsigned char field_byte;
+ DBusString contained_type;
+ char buf[2];
+
+ start = writer->value_pos;
+ padding = _dbus_string_get_length (writer->value_str) - start;
+
+ if (!_dbus_type_writer_recurse (writer, DBUS_TYPE_STRUCT,
+ NULL, 0, &sub))
+ goto append_failed;
+
+ field_byte = field;
+ if (!_dbus_type_writer_write_basic (&sub, DBUS_TYPE_BYTE,
+ &field_byte))
+ goto append_failed;
+
+ buf[0] = type;
+ buf[1] = '\0';
+ _dbus_string_init_const_len (&contained_type, buf, 1);
+
+ if (!_dbus_type_writer_recurse (&sub, DBUS_TYPE_VARIANT,
+ &contained_type, 0, &variant))
+ goto append_failed;
+
+ if (!_dbus_type_writer_write_basic (&variant, type, value))
+ goto append_failed;
+
+ if (!_dbus_type_writer_unrecurse (&sub, &variant))
+ goto append_failed;
+
+ if (!_dbus_type_writer_unrecurse (writer, &sub))
+ goto append_failed;
+
+ return TRUE;
+
+ append_failed:
+ _dbus_string_delete (writer->value_str,
+ start,
+ _dbus_string_get_length (writer->value_str) - start - padding);
+ return FALSE;
+}
+
+/**
+ * Sets a struct of { byte, variant } with the given basic type.
+ *
+ * @param reader the reader (should be iterating over the array pointing at the field to set)
+ * @param field the header field
+ * @param type the type of the value
+ * @param value the value as for _dbus_marshal_set_basic()
+ * @param realign_root where to realign from
+ * @returns #FALSE if no memory
+ */
+static dbus_bool_t
+set_basic_field (DBusTypeReader *reader,
+ int field,
+ int type,
+ const void *value,
+ const DBusTypeReader *realign_root)
+{
+ DBusTypeReader sub;
+ DBusTypeReader variant;
+
+ _dbus_type_reader_recurse (reader, &sub);
+
+ _dbus_assert (_dbus_type_reader_get_current_type (&sub) == DBUS_TYPE_BYTE);
+#ifndef DBUS_DISABLE_ASSERT
+ {
+ unsigned char v_BYTE;
+ _dbus_type_reader_read_basic (&sub, &v_BYTE);
+ _dbus_assert (((int) v_BYTE) == field);
+ }
+#endif
+
+ if (!_dbus_type_reader_next (&sub))
+ _dbus_assert_not_reached ("no variant field?");
+
+ _dbus_type_reader_recurse (&sub, &variant);
+ _dbus_assert (_dbus_type_reader_get_current_type (&variant) == type);
+
+ if (!_dbus_type_reader_set_basic (&variant, value, realign_root))
+ return FALSE;
+
+ return TRUE;
+}
+
+/**
+ * Gets the type of the message.
+ *
+ * @param header the header
+ * @returns the type
+ */
+int
+_dbus_header_get_message_type (DBusHeader *header)
+{
+ int type;
+
+ type = _dbus_string_get_byte (&header->data, TYPE_OFFSET);
+ _dbus_assert (type != DBUS_MESSAGE_TYPE_INVALID);
+
+ return type;
+}
+
+/**
+ * Sets the serial number of a header. This can only be done once on
+ * a header.
+ *
+ * @param header the header
+ * @param serial the serial
+ */
+void
+_dbus_header_set_serial (DBusHeader *header,
+ dbus_uint32_t serial)
+{
+ /* we use this function to set the serial on outgoing
+ * messages, and to reset the serial in dbus_message_copy;
+ * this assertion should catch a double-set on outgoing.
+ */
+ _dbus_assert (_dbus_header_get_serial (header) == 0 ||
+ serial == 0);
+
+ _dbus_marshal_set_uint32 (&header->data,
+ SERIAL_OFFSET,
+ serial,
+ _dbus_header_get_byte_order (header));
+}
+
+/**
+ * See dbus_message_get_serial()
+ *
+ * @param header the header
+ * @returns the client serial
+ */
+dbus_uint32_t
+_dbus_header_get_serial (DBusHeader *header)
+{
+ return _dbus_marshal_read_uint32 (&header->data,
+ SERIAL_OFFSET,
+ _dbus_header_get_byte_order (header),
+ NULL);
+}
+
+/**
+ * Re-initializes a header that was previously initialized and never
+ * freed. After this, to make the header valid you have to call
+ * _dbus_header_create().
+ *
+ * @param header header to re-initialize
+ */
+void
+_dbus_header_reinit (DBusHeader *header)
+{
+ _dbus_string_set_length (&header->data, 0);
+
+ header->padding = 0;
+
+ _dbus_header_cache_invalidate_all (header);
+}
+
+/**
+ * Initializes a header, but doesn't prepare it for use;
+ * to make the header valid, you have to call _dbus_header_create().
+ *
+ * @param header header to initialize
+ * @returns #FALSE if not enough memory
+ */
+dbus_bool_t
+_dbus_header_init (DBusHeader *header)
+{
+ if (!_dbus_string_init_preallocated (&header->data, 32))
+ return FALSE;
+
+ _dbus_header_reinit (header);
+
+ return TRUE;
+}
+
+/**
+ * Frees a header.
+ *
+ * @param header the header
+ */
+void
+_dbus_header_free (DBusHeader *header)
+{
+ _dbus_string_free (&header->data);
+}
+
+/**
+ * Initializes dest with a copy of the given header.
+ * Resets the message serial to 0 on the copy.
+ *
+ * @param header header to copy
+ * @param dest destination for copy
+ * @returns #FALSE if not enough memory
+ */
+dbus_bool_t
+_dbus_header_copy (const DBusHeader *header,
+ DBusHeader *dest)
+{
+ *dest = *header;
+
+ if (!_dbus_string_init_preallocated (&dest->data,
+ _dbus_string_get_length (&header->data)))
+ return FALSE;
+
+ if (!_dbus_string_copy (&header->data, 0, &dest->data, 0))
+ {
+ _dbus_string_free (&dest->data);
+ return FALSE;
+ }
+
+ /* Reset the serial */
+ _dbus_header_set_serial (dest, 0);
+
+ return TRUE;
+}
+
+/**
+ * Fills in the primary fields of the header, so the header is ready
+ * for use. #NULL may be specified for some or all of the fields to
+ * avoid adding those fields. Some combinations of fields don't make
+ * sense, and passing them in will trigger an assertion failure.
+ *
+ * @param header the header
+ * @param byte_order byte order of the header
+ * @param message_type the message type
+ * @param destination destination field or #NULL
+ * @param path path field or #NULL
+ * @param interface interface field or #NULL
+ * @param member member field or #NULL
+ * @param error_name error name or #NULL
+ * @returns #FALSE if not enough memory
+ */
+dbus_bool_t
+_dbus_header_create (DBusHeader *header,
+ int byte_order,
+ int message_type,
+ const char *destination,
+ const char *path,
+ const char *interface,
+ const char *member,
+ const char *error_name)
+{
+ unsigned char v_BYTE;
+ dbus_uint32_t v_UINT32;
+ DBusTypeWriter writer;
+ DBusTypeWriter array;
+
+ _dbus_assert (byte_order == DBUS_LITTLE_ENDIAN ||
+ byte_order == DBUS_BIG_ENDIAN);
+ _dbus_assert (((interface || message_type != DBUS_MESSAGE_TYPE_SIGNAL) && member) ||
+ (error_name) ||
+ !(interface || member || error_name));
+ _dbus_assert (_dbus_string_get_length (&header->data) == 0);
+
+ if (!reserve_header_padding (header))
+ return FALSE;
+
+ _dbus_type_writer_init_values_only (&writer, byte_order,
+ &_dbus_header_signature_str, 0,
+ &header->data,
+ HEADER_END_BEFORE_PADDING (header));
+
+ v_BYTE = byte_order;
+ if (!_dbus_type_writer_write_basic (&writer, DBUS_TYPE_BYTE,
+ &v_BYTE))
+ goto oom;
+
+ v_BYTE = message_type;
+ if (!_dbus_type_writer_write_basic (&writer, DBUS_TYPE_BYTE,
+ &v_BYTE))
+ goto oom;
+
+ v_BYTE = 0; /* flags */
+ if (!_dbus_type_writer_write_basic (&writer, DBUS_TYPE_BYTE,
+ &v_BYTE))
+ goto oom;
+
+ v_BYTE = DBUS_MAJOR_PROTOCOL_VERSION;
+ if (!_dbus_type_writer_write_basic (&writer, DBUS_TYPE_BYTE,
+ &v_BYTE))
+ goto oom;
+
+ v_UINT32 = 0; /* body length */
+ if (!_dbus_type_writer_write_basic (&writer, DBUS_TYPE_UINT32,
+ &v_UINT32))
+ goto oom;
+
+ v_UINT32 = 0; /* serial */
+ if (!_dbus_type_writer_write_basic (&writer, DBUS_TYPE_UINT32,
+ &v_UINT32))
+ goto oom;
+
+ if (!_dbus_type_writer_recurse (&writer, DBUS_TYPE_ARRAY,
+ &_dbus_header_signature_str,
+ FIELDS_ARRAY_SIGNATURE_OFFSET,
+ &array))
+ goto oom;
+
+ /* Marshal all the fields (Marshall Fields?) */
+
+ if (path != NULL)
+ {
+ if (!write_basic_field (&array,
+ DBUS_HEADER_FIELD_PATH,
+ DBUS_TYPE_OBJECT_PATH,
+ &path))
+ goto oom;
+ }
+
+ if (destination != NULL)
+ {
+ if (!write_basic_field (&array,
+ DBUS_HEADER_FIELD_DESTINATION,
+ DBUS_TYPE_STRING,
+ &destination))
+ goto oom;
+ }
+
+ /* Note that test/message.c relies on this being in the middle of the
+ * message: if you change the order of serialization here (but why
+ * would you?), please find some other way to retain test coverage. */
+ if (interface != NULL)
+ {
+ if (!write_basic_field (&array,
+ DBUS_HEADER_FIELD_INTERFACE,
+ DBUS_TYPE_STRING,
+ &interface))
+ goto oom;
+ }
+
+ if (member != NULL)
+ {
+ if (!write_basic_field (&array,
+ DBUS_HEADER_FIELD_MEMBER,
+ DBUS_TYPE_STRING,
+ &member))
+ goto oom;
+ }
+
+ if (error_name != NULL)
+ {
+ if (!write_basic_field (&array,
+ DBUS_HEADER_FIELD_ERROR_NAME,
+ DBUS_TYPE_STRING,
+ &error_name))
+ goto oom;
+ }
+
+ if (!_dbus_type_writer_unrecurse (&writer, &array))
+ goto oom;
+
+ correct_header_padding (header);
+
+ return TRUE;
+
+ oom:
+ _dbus_string_delete (&header->data, 0,
+ _dbus_string_get_length (&header->data) - header->padding);
+ correct_header_padding (header);
+
+ return FALSE;
+}
+
+/**
+ * Given data long enough to contain the length of the message body
+ * and the fields array, check whether the data is long enough to
+ * contain the entire message (assuming the claimed lengths are
+ * accurate). Also checks that the lengths are in sanity parameters.
+ *
+ * @param max_message_length maximum length of a valid message
+ * @param validity return location for why the data is invalid if it is
+ * @param byte_order return location for byte order
+ * @param fields_array_len return location for claimed fields array length
+ * @param header_len return location for claimed header length
+ * @param body_len return location for claimed body length
+ * @param str the data
+ * @param start start of data, 8-aligned
+ * @param len length of data
+ * @returns #TRUE if the data is long enough for the claimed length, and the lengths were valid
+ */
+dbus_bool_t
+_dbus_header_have_message_untrusted (int max_message_length,
+ DBusValidity *validity,
+ int *byte_order,
+ int *fields_array_len,
+ int *header_len,
+ int *body_len,
+ const DBusString *str,
+ int start,
+ int len)
+
+{
+ dbus_uint32_t header_len_unsigned;
+ dbus_uint32_t fields_array_len_unsigned;
+ dbus_uint32_t body_len_unsigned;
+
+ _dbus_assert (start >= 0);
+ _dbus_assert (start < _DBUS_INT32_MAX / 2);
+ _dbus_assert (len >= 0);
+
+ _dbus_assert (start == (int) _DBUS_ALIGN_VALUE (start, 8));
+
+ *byte_order = _dbus_string_get_byte (str, start + BYTE_ORDER_OFFSET);
+
+ if (*byte_order != DBUS_LITTLE_ENDIAN && *byte_order != DBUS_BIG_ENDIAN)
+ {
+ *validity = DBUS_INVALID_BAD_BYTE_ORDER;
+ return FALSE;
+ }
+
+ _dbus_assert (FIELDS_ARRAY_LENGTH_OFFSET + 4 <= len);
+ fields_array_len_unsigned = _dbus_marshal_read_uint32 (str, start + FIELDS_ARRAY_LENGTH_OFFSET,
+ *byte_order, NULL);
+
+ if (fields_array_len_unsigned > (unsigned) max_message_length)
+ {
+ *validity = DBUS_INVALID_INSANE_FIELDS_ARRAY_LENGTH;
+ return FALSE;
+ }
+
+ _dbus_assert (BODY_LENGTH_OFFSET + 4 < len);
+ body_len_unsigned = _dbus_marshal_read_uint32 (str, start + BODY_LENGTH_OFFSET,
+ *byte_order, NULL);
+
+ if (body_len_unsigned > (unsigned) max_message_length)
+ {
+ *validity = DBUS_INVALID_INSANE_BODY_LENGTH;
+ return FALSE;
+ }
+
+ header_len_unsigned = FIRST_FIELD_OFFSET + fields_array_len_unsigned;
+ header_len_unsigned = _DBUS_ALIGN_VALUE (header_len_unsigned, 8);
+
+ /* overflow should be impossible since the lengths aren't allowed to
+ * be huge.
+ */
+ _dbus_assert (max_message_length < _DBUS_INT32_MAX / 2);
+ if (body_len_unsigned + header_len_unsigned > (unsigned) max_message_length)
+ {
+ *validity = DBUS_INVALID_MESSAGE_TOO_LONG;
+ return FALSE;
+ }
+
+ _dbus_assert (body_len_unsigned < (unsigned) _DBUS_INT32_MAX);
+ _dbus_assert (fields_array_len_unsigned < (unsigned) _DBUS_INT32_MAX);
+ _dbus_assert (header_len_unsigned < (unsigned) _DBUS_INT32_MAX);
+
+ *body_len = body_len_unsigned;
+ *fields_array_len = fields_array_len_unsigned;
+ *header_len = header_len_unsigned;
+
+ *validity = DBUS_VALID;
+
+ _dbus_verbose ("have %d bytes, need body %u + header %u = %u\n",
+ len, body_len_unsigned, header_len_unsigned,
+ body_len_unsigned + header_len_unsigned);
+
+ return (body_len_unsigned + header_len_unsigned) <= (unsigned) len;
+}
+
+static DBusValidity
+check_mandatory_fields (DBusHeader *header)
+{
+#define REQUIRE_FIELD(name) do { if (header->fields[DBUS_HEADER_FIELD_##name].value_pos < 0) return DBUS_INVALID_MISSING_##name; } while (0)
+
+ switch (_dbus_header_get_message_type (header))
+ {
+ case DBUS_MESSAGE_TYPE_SIGNAL:
+ REQUIRE_FIELD (INTERFACE);
+ /* FALL THRU - signals also require the path and member */
+ case DBUS_MESSAGE_TYPE_METHOD_CALL:
+ REQUIRE_FIELD (PATH);
+ REQUIRE_FIELD (MEMBER);
+ break;
+ case DBUS_MESSAGE_TYPE_ERROR:
+ REQUIRE_FIELD (ERROR_NAME);
+ REQUIRE_FIELD (REPLY_SERIAL);
+ break;
+ case DBUS_MESSAGE_TYPE_METHOD_RETURN:
+ REQUIRE_FIELD (REPLY_SERIAL);
+ break;
+ default:
+ /* other message types allowed but ignored */
+ break;
+ }
+
+ return DBUS_VALID;
+}
+
+static DBusValidity
+load_and_validate_field (DBusHeader *header,
+ int field,
+ DBusTypeReader *variant_reader)
+{
+ int type;
+ int expected_type;
+ const DBusString *value_str;
+ int value_pos;
+ int str_data_pos;
+ dbus_uint32_t v_UINT32;
+ int bad_string_code;
+ dbus_bool_t (* string_validation_func) (const DBusString *str,
+ int start, int len);
+
+ /* Supposed to have been checked already */
+ _dbus_assert (field <= DBUS_HEADER_FIELD_LAST);
+ _dbus_assert (field != DBUS_HEADER_FIELD_INVALID);
+
+ /* Before we can cache a field, we need to know it has the right type */
+ type = _dbus_type_reader_get_current_type (variant_reader);
+
+ _dbus_assert (_dbus_header_field_types[field].code == field);
+
+ expected_type = EXPECTED_TYPE_OF_FIELD (field);
+ if (type != expected_type)
+ {
+ _dbus_verbose ("Field %d should have type %d but has %d\n",
+ field, expected_type, type);
+ return DBUS_INVALID_HEADER_FIELD_HAS_WRONG_TYPE;
+ }
+
+ /* If the field was provided twice, we aren't happy */
+ if (header->fields[field].value_pos >= 0)
+ {
+ _dbus_verbose ("Header field %d seen a second time\n", field);
+ return DBUS_INVALID_HEADER_FIELD_APPEARS_TWICE;
+ }
+
+ /* Now we can cache and look at the field content */
+ _dbus_verbose ("initially caching field %d\n", field);
+ _dbus_header_cache_one (header, field, variant_reader);
+
+ string_validation_func = NULL;
+
+ /* make compiler happy that all this is initialized */
+ v_UINT32 = 0;
+ value_str = NULL;
+ value_pos = -1;
+ str_data_pos = -1;
+ bad_string_code = DBUS_VALID;
+
+ if (expected_type == DBUS_TYPE_UINT32)
+ {
+ _dbus_header_get_field_basic (header, field, expected_type,
+ &v_UINT32);
+ }
+ else if (expected_type == DBUS_TYPE_STRING ||
+ expected_type == DBUS_TYPE_OBJECT_PATH ||
+ expected_type == DBUS_TYPE_SIGNATURE)
+ {
+ _dbus_header_get_field_raw (header, field,
+ &value_str, &value_pos);
+ str_data_pos = _DBUS_ALIGN_VALUE (value_pos, 4) + 4;
+ }
+ else
+ {
+ _dbus_assert_not_reached ("none of the known fields should have this type");
+ }
+
+ switch (field)
+ {
+ case DBUS_HEADER_FIELD_DESTINATION:
+ string_validation_func = _dbus_validate_bus_name;
+ bad_string_code = DBUS_INVALID_BAD_DESTINATION;
+ break;
+ case DBUS_HEADER_FIELD_INTERFACE:
+ string_validation_func = _dbus_validate_interface;
+ bad_string_code = DBUS_INVALID_BAD_INTERFACE;
+
+ if (_dbus_string_equal_substring (&_dbus_local_interface_str,
+ 0,
+ _dbus_string_get_length (&_dbus_local_interface_str),
+ value_str, str_data_pos))
+ {
+ _dbus_verbose ("Message is on the local interface\n");
+ return DBUS_INVALID_USES_LOCAL_INTERFACE;
+ }
+ break;
+
+ case DBUS_HEADER_FIELD_MEMBER:
+ string_validation_func = _dbus_validate_member;
+ bad_string_code = DBUS_INVALID_BAD_MEMBER;
+ break;
+
+ case DBUS_HEADER_FIELD_ERROR_NAME:
+ string_validation_func = _dbus_validate_error_name;
+ bad_string_code = DBUS_INVALID_BAD_ERROR_NAME;
+ break;
+
+ case DBUS_HEADER_FIELD_SENDER:
+ string_validation_func = _dbus_validate_bus_name;
+ bad_string_code = DBUS_INVALID_BAD_SENDER;
+ break;
+
+ case DBUS_HEADER_FIELD_PATH:
+ /* OBJECT_PATH was validated generically due to its type */
+ string_validation_func = NULL;
+
+ if (_dbus_string_equal_substring (&_dbus_local_path_str,
+ 0,
+ _dbus_string_get_length (&_dbus_local_path_str),
+ value_str, str_data_pos))
+ {
+ _dbus_verbose ("Message is from the local path\n");
+ return DBUS_INVALID_USES_LOCAL_PATH;
+ }
+ break;
+
+ case DBUS_HEADER_FIELD_REPLY_SERIAL:
+ /* Can't be 0 */
+ if (v_UINT32 == 0)
+ {
+ return DBUS_INVALID_BAD_SERIAL;
+ }
+ break;
+
+ case DBUS_HEADER_FIELD_UNIX_FDS:
+ /* Every value makes sense */
+ break;
+
+ case DBUS_HEADER_FIELD_SIGNATURE:
+ /* SIGNATURE validated generically due to its type */
+ string_validation_func = NULL;
+ break;
+
+ case DBUS_HEADER_FIELD_CONTAINER_INSTANCE:
+ /* OBJECT_PATH was validated generically due to its type */
+ string_validation_func = NULL;
+ break;
+
+ default:
+ _dbus_assert_not_reached ("unknown field shouldn't be seen here");
+ break;
+ }
+
+ if (string_validation_func)
+ {
+ dbus_uint32_t len;
+
+ _dbus_assert (bad_string_code != DBUS_VALID);
+
+ len = _dbus_marshal_read_uint32 (value_str, value_pos,
+ _dbus_header_get_byte_order (header),
+ NULL);
+
+#if 0
+ _dbus_verbose ("Validating string header field; code %d if fails\n",
+ bad_string_code);
+#endif
+ if (!(*string_validation_func) (value_str, str_data_pos, len))
+ return bad_string_code;
+ }
+
+ return DBUS_VALID;
+}
+
+/**
+ * Creates a message header from potentially-untrusted data. The
+ * return value is #TRUE if there was enough memory and the data was
+ * valid. If it returns #TRUE, the header will be created. If it
+ * returns #FALSE and *validity == #DBUS_VALIDITY_UNKNOWN_OOM_ERROR,
+ * then there wasn't enough memory. If it returns #FALSE
+ * and *validity != #DBUS_VALIDITY_UNKNOWN_OOM_ERROR then the data was
+ * invalid.
+ *
+ * The byte_order, fields_array_len, and body_len args should be from
+ * _dbus_header_have_message_untrusted(). Validation performed in
+ * _dbus_header_have_message_untrusted() is assumed to have been
+ * already done.
+ *
+ * @param header the header (must be initialized)
+ * @param mode whether to do validation
+ * @param validity return location for invalidity reason
+ * @param byte_order byte order from header
+ * @param fields_array_len claimed length of fields array
+ * @param body_len claimed length of body
+ * @param header_len claimed length of header
+ * @param str a string starting with the header
+ * @returns #FALSE if no memory or data was invalid, #TRUE otherwise
+ */
+dbus_bool_t
+_dbus_header_load (DBusHeader *header,
+ DBusValidationMode mode,
+ DBusValidity *validity,
+ int byte_order,
+ int fields_array_len,
+ int header_len,
+ int body_len,
+ const DBusString *str)
+{
+ int leftover;
+ DBusValidity v;
+ DBusTypeReader reader;
+ DBusTypeReader array_reader;
+ unsigned char v_byte;
+ dbus_uint32_t v_uint32;
+ dbus_uint32_t serial;
+ int padding_start;
+ int padding_len;
+ int i;
+ int len;
+
+ len = _dbus_string_get_length (str);
+
+ _dbus_assert (header_len <= len);
+ _dbus_assert (_dbus_string_get_length (&header->data) == 0);
+
+ if (!_dbus_string_copy_len (str, 0, header_len, &header->data, 0))
+ {
+ _dbus_verbose ("Failed to copy buffer into new header\n");
+ *validity = DBUS_VALIDITY_UNKNOWN_OOM_ERROR;
+ return FALSE;
+ }
+
+ if (mode == DBUS_VALIDATION_MODE_WE_TRUST_THIS_DATA_ABSOLUTELY)
+ {
+ leftover = len - header_len - body_len;
+ }
+ else
+ {
+ v = _dbus_validate_body_with_reason (&_dbus_header_signature_str, 0,
+ byte_order,
+ &leftover,
+ str, 0, len);
+
+ if (v != DBUS_VALID)
+ {
+ *validity = v;
+ goto invalid;
+ }
+ }
+
+ _dbus_assert (leftover < len);
+
+ padding_len = header_len - (FIRST_FIELD_OFFSET + fields_array_len);
+ padding_start = FIRST_FIELD_OFFSET + fields_array_len;
+ _dbus_assert (header_len == (int) _DBUS_ALIGN_VALUE (padding_start, 8));
+ _dbus_assert (header_len == padding_start + padding_len);
+
+ if (mode != DBUS_VALIDATION_MODE_WE_TRUST_THIS_DATA_ABSOLUTELY)
+ {
+ if (!_dbus_string_validate_nul (str, padding_start, padding_len))
+ {
+ *validity = DBUS_INVALID_ALIGNMENT_PADDING_NOT_NUL;
+ goto invalid;
+ }
+ }
+
+ header->padding = padding_len;
+
+ if (mode == DBUS_VALIDATION_MODE_WE_TRUST_THIS_DATA_ABSOLUTELY)
+ {
+ *validity = DBUS_VALID;
+ return TRUE;
+ }
+
+ /* We now know the data is well-formed, but we have to check that
+ * it's valid.
+ */
+
+ _dbus_type_reader_init (&reader,
+ byte_order,
+ &_dbus_header_signature_str, 0,
+ str, 0);
+
+ /* BYTE ORDER */
+ _dbus_assert (_dbus_type_reader_get_current_type (&reader) == DBUS_TYPE_BYTE);
+ _dbus_assert (_dbus_type_reader_get_value_pos (&reader) == BYTE_ORDER_OFFSET);
+ _dbus_type_reader_read_basic (&reader, &v_byte);
+ _dbus_type_reader_next (&reader);
+
+ _dbus_assert (v_byte == byte_order);
+
+ /* MESSAGE TYPE */
+ _dbus_assert (_dbus_type_reader_get_current_type (&reader) == DBUS_TYPE_BYTE);
+ _dbus_assert (_dbus_type_reader_get_value_pos (&reader) == TYPE_OFFSET);
+ _dbus_type_reader_read_basic (&reader, &v_byte);
+ _dbus_type_reader_next (&reader);
+
+ /* unknown message types are supposed to be ignored, so only validation here is
+ * that it isn't invalid
+ */
+ if (v_byte == DBUS_MESSAGE_TYPE_INVALID)
+ {
+ *validity = DBUS_INVALID_BAD_MESSAGE_TYPE;
+ goto invalid;
+ }
+
+ /* FLAGS */
+ _dbus_assert (_dbus_type_reader_get_current_type (&reader) == DBUS_TYPE_BYTE);
+ _dbus_assert (_dbus_type_reader_get_value_pos (&reader) == FLAGS_OFFSET);
+ _dbus_type_reader_read_basic (&reader, &v_byte);
+ _dbus_type_reader_next (&reader);
+
+ /* unknown flags should be ignored */
+
+ /* PROTOCOL VERSION */
+ _dbus_assert (_dbus_type_reader_get_current_type (&reader) == DBUS_TYPE_BYTE);
+ _dbus_assert (_dbus_type_reader_get_value_pos (&reader) == VERSION_OFFSET);
+ _dbus_type_reader_read_basic (&reader, &v_byte);
+ _dbus_type_reader_next (&reader);
+
+ if (v_byte != DBUS_MAJOR_PROTOCOL_VERSION)
+ {
+ *validity = DBUS_INVALID_BAD_PROTOCOL_VERSION;
+ goto invalid;
+ }
+
+ /* BODY LENGTH */
+ _dbus_assert (_dbus_type_reader_get_current_type (&reader) == DBUS_TYPE_UINT32);
+ _dbus_assert (_dbus_type_reader_get_value_pos (&reader) == BODY_LENGTH_OFFSET);
+ _dbus_type_reader_read_basic (&reader, &v_uint32);
+ _dbus_type_reader_next (&reader);
+
+ _dbus_assert (body_len == (signed) v_uint32);
+
+ /* SERIAL */
+ _dbus_assert (_dbus_type_reader_get_current_type (&reader) == DBUS_TYPE_UINT32);
+ _dbus_assert (_dbus_type_reader_get_value_pos (&reader) == SERIAL_OFFSET);
+ _dbus_type_reader_read_basic (&reader, &serial);
+ _dbus_type_reader_next (&reader);
+
+ if (serial == 0)
+ {
+ *validity = DBUS_INVALID_BAD_SERIAL;
+ goto invalid;
+ }
+
+ _dbus_assert (_dbus_type_reader_get_current_type (&reader) == DBUS_TYPE_ARRAY);
+ _dbus_assert (_dbus_type_reader_get_value_pos (&reader) == FIELDS_ARRAY_LENGTH_OFFSET);
+
+ _dbus_type_reader_recurse (&reader, &array_reader);
+ while (_dbus_type_reader_get_current_type (&array_reader) != DBUS_TYPE_INVALID)
+ {
+ DBusTypeReader struct_reader;
+ DBusTypeReader variant_reader;
+ unsigned char field_code;
+
+ _dbus_assert (_dbus_type_reader_get_current_type (&array_reader) == DBUS_TYPE_STRUCT);
+
+ _dbus_type_reader_recurse (&array_reader, &struct_reader);
+
+ _dbus_assert (_dbus_type_reader_get_current_type (&struct_reader) == DBUS_TYPE_BYTE);
+ _dbus_type_reader_read_basic (&struct_reader, &field_code);
+ _dbus_type_reader_next (&struct_reader);
+
+ if (field_code == DBUS_HEADER_FIELD_INVALID)
+ {
+ _dbus_verbose ("invalid header field code\n");
+ *validity = DBUS_INVALID_HEADER_FIELD_CODE;
+ goto invalid;
+ }
+
+ if (field_code > DBUS_HEADER_FIELD_LAST)
+ {
+ _dbus_verbose ("unknown header field code %d, skipping\n",
+ field_code);
+ goto next_field;
+ }
+
+ _dbus_assert (_dbus_type_reader_get_current_type (&struct_reader) == DBUS_TYPE_VARIANT);
+ _dbus_type_reader_recurse (&struct_reader, &variant_reader);
+
+ v = load_and_validate_field (header, field_code, &variant_reader);
+ if (v != DBUS_VALID)
+ {
+ _dbus_verbose ("Field %d was invalid\n", field_code);
+ *validity = v;
+ goto invalid;
+ }
+
+ next_field:
+ _dbus_type_reader_next (&array_reader);
+ }
+
+ /* Anything we didn't fill in is now known not to exist */
+ i = 0;
+ while (i <= DBUS_HEADER_FIELD_LAST)
+ {
+ if (header->fields[i].value_pos == _DBUS_HEADER_FIELD_VALUE_UNKNOWN)
+ header->fields[i].value_pos = _DBUS_HEADER_FIELD_VALUE_NONEXISTENT;
+ ++i;
+ }
+
+ v = check_mandatory_fields (header);
+ if (v != DBUS_VALID)
+ {
+ _dbus_verbose ("Mandatory fields were missing, code %d\n", v);
+ *validity = v;
+ goto invalid;
+ }
+
+ *validity = DBUS_VALID;
+ return TRUE;
+
+ invalid:
+ _dbus_string_set_length (&header->data, 0);
+ return FALSE;
+}
+
+/**
+ * Fills in the correct body length.
+ *
+ * @param header the header
+ * @param body_len the length of the body
+ */
+void
+_dbus_header_update_lengths (DBusHeader *header,
+ int body_len)
+{
+ _dbus_marshal_set_uint32 (&header->data,
+ BODY_LENGTH_OFFSET,
+ body_len,
+ _dbus_header_get_byte_order (header));
+}
+
+/**
+ * Try to find the given field.
+ *
+ * @param header the header
+ * @param field the field code
+ * @param reader a type reader; on success this is left pointing at the struct
+ * (uv) for the field, while on failure it is left pointing into empty space
+ * at the end of the header fields
+ * @param realign_root another type reader; on success or failure it is left
+ * pointing to the beginning of the array of fields (i.e. the thing that might
+ * need realigning)
+ * @returns #TRUE on success
+ */
+static dbus_bool_t
+find_field_for_modification (DBusHeader *header,
+ int field,
+ DBusTypeReader *reader,
+ DBusTypeReader *realign_root)
+{
+ dbus_bool_t retval;
+
+ retval = FALSE;
+
+ _dbus_type_reader_init (realign_root,
+ _dbus_header_get_byte_order (header),
+ &_dbus_header_signature_str,
+ FIELDS_ARRAY_SIGNATURE_OFFSET,
+ &header->data,
+ FIELDS_ARRAY_LENGTH_OFFSET);
+
+ _dbus_type_reader_recurse (realign_root, reader);
+
+ while (_dbus_type_reader_get_current_type (reader) != DBUS_TYPE_INVALID)
+ {
+ DBusTypeReader sub;
+ unsigned char field_code;
+
+ _dbus_type_reader_recurse (reader, &sub);
+
+ _dbus_assert (_dbus_type_reader_get_current_type (&sub) == DBUS_TYPE_BYTE);
+ _dbus_type_reader_read_basic (&sub, &field_code);
+
+ if (field_code == (unsigned) field)
+ {
+ _dbus_assert (_dbus_type_reader_get_current_type (reader) == DBUS_TYPE_STRUCT);
+ retval = TRUE;
+ goto done;
+ }
+
+ _dbus_type_reader_next (reader);
+ }
+
+ done:
+ return retval;
+}
+
+/**
+ * Sets the value of a field with basic type. If the value is a string
+ * value, it isn't allowed to be #NULL. If the field doesn't exist,
+ * it will be created.
+ *
+ * @param header the header
+ * @param field the field to set
+ * @param type the type of the value
+ * @param value the value as for _dbus_marshal_set_basic()
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_header_set_field_basic (DBusHeader *header,
+ int field,
+ int type,
+ const void *value)
+{
+ _dbus_assert (field <= DBUS_HEADER_FIELD_LAST);
+
+ if (!reserve_header_padding (header))
+ return FALSE;
+
+ /* If the field exists we set, otherwise we append */
+ if (_dbus_header_cache_check (header, field))
+ {
+ DBusTypeReader reader;
+ DBusTypeReader realign_root;
+
+ if (!find_field_for_modification (header, field,
+ &reader, &realign_root))
+ _dbus_assert_not_reached ("field was marked present in cache but wasn't found");
+
+ if (!set_basic_field (&reader, field, type, value, &realign_root))
+ return FALSE;
+ }
+ else
+ {
+ DBusTypeWriter writer;
+ DBusTypeWriter array;
+
+ _dbus_type_writer_init_values_only (&writer,
+ _dbus_header_get_byte_order (header),
+ &_dbus_header_signature_str,
+ FIELDS_ARRAY_SIGNATURE_OFFSET,
+ &header->data,
+ FIELDS_ARRAY_LENGTH_OFFSET);
+
+ /* recurse into array without creating a new length, and jump to
+ * end of array.
+ */
+ if (!_dbus_type_writer_append_array (&writer,
+ &_dbus_header_signature_str,
+ FIELDS_ARRAY_ELEMENT_SIGNATURE_OFFSET,
+ &array))
+ _dbus_assert_not_reached ("recurse into ARRAY should not have used memory");
+
+ _dbus_assert (array.u.array.len_pos == FIELDS_ARRAY_LENGTH_OFFSET);
+ _dbus_assert (array.u.array.start_pos == FIRST_FIELD_OFFSET);
+ _dbus_assert (array.value_pos == HEADER_END_BEFORE_PADDING (header));
+
+ if (!write_basic_field (&array,
+ field, type, value))
+ return FALSE;
+
+ if (!_dbus_type_writer_unrecurse (&writer, &array))
+ _dbus_assert_not_reached ("unrecurse from ARRAY should not have used memory");
+ }
+
+ correct_header_padding (header);
+
+ /* We could be smarter about this (only invalidate fields after the
+ * one we modified, or even only if the one we modified changed
+ * length). But this hack is a start.
+ */
+ _dbus_header_cache_invalidate_all (header);
+
+ return TRUE;
+}
+
+/**
+ * Gets the value of a field with basic type. If the field
+ * doesn't exist, returns #FALSE, otherwise returns #TRUE.
+ *
+ * @param header the header
+ * @param field the field to get
+ * @param type the type of the value
+ * @param value the value as for _dbus_marshal_read_basic()
+ * @returns #FALSE if the field doesn't exist
+ */
+dbus_bool_t
+_dbus_header_get_field_basic (DBusHeader *header,
+ int field,
+ int type,
+ void *value)
+{
+ _dbus_assert (field != DBUS_HEADER_FIELD_INVALID);
+ _dbus_assert (field <= DBUS_HEADER_FIELD_LAST);
+ _dbus_assert (_dbus_header_field_types[field].code == field);
+ /* in light of this you might ask why the type is passed in;
+ * the only rationale I can think of is so the caller has
+ * to specify its expectation and breaks if we change it
+ */
+ _dbus_assert (type == EXPECTED_TYPE_OF_FIELD (field));
+
+ if (!_dbus_header_cache_check (header, field))
+ return FALSE;
+
+ _dbus_assert (header->fields[field].value_pos >= 0);
+
+ _dbus_marshal_read_basic (&header->data,
+ header->fields[field].value_pos,
+ type, value, _dbus_header_get_byte_order (header),
+ NULL);
+
+ return TRUE;
+}
+
+/**
+ * Gets the raw marshaled data for a field. If the field doesn't
+ * exist, returns #FALSE, otherwise returns #TRUE. Returns the start
+ * of the marshaled data, i.e. usually the byte where the length
+ * starts (for strings and arrays) or for basic types just the value
+ * itself.
+ *
+ * @param header the header
+ * @param field the field to get
+ * @param str return location for the data string
+ * @param pos return location for start of field value
+ * @returns #FALSE if the field doesn't exist
+ */
+dbus_bool_t
+_dbus_header_get_field_raw (DBusHeader *header,
+ int field,
+ const DBusString **str,
+ int *pos)
+{
+ if (!_dbus_header_cache_check (header, field))
+ return FALSE;
+
+ if (str)
+ *str = &header->data;
+ if (pos)
+ *pos = header->fields[field].value_pos;
+
+ return TRUE;
+}
+
+/**
+ * Deletes a field, if it exists.
+ *
+ * @param header the header
+ * @param field the field to delete
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_header_delete_field (DBusHeader *header,
+ int field)
+{
+ DBusTypeReader reader;
+ DBusTypeReader realign_root;
+
+ if (_dbus_header_cache_known_nonexistent (header, field))
+ return TRUE; /* nothing to do */
+
+ /* Scan to the field we want, delete and realign, reappend
+ * padding. Field may turn out not to exist.
+ */
+ if (!find_field_for_modification (header, field,
+ &reader, &realign_root))
+ return TRUE; /* nothing to do */
+
+ if (!reserve_header_padding (header))
+ return FALSE;
+
+ if (!_dbus_type_reader_delete (&reader,
+ &realign_root))
+ return FALSE;
+
+ correct_header_padding (header);
+
+ _dbus_header_cache_invalidate_all (header);
+
+ _dbus_assert (!_dbus_header_cache_check (header, field)); /* Expensive assertion ... */
+
+ return TRUE;
+}
+
+/**
+ * Toggles a message flag bit, turning on the bit if value = TRUE and
+ * flipping it off if value = FALSE.
+ *
+ * @param header the header
+ * @param flag the message flag to toggle
+ * @param value toggle on or off
+ */
+void
+_dbus_header_toggle_flag (DBusHeader *header,
+ dbus_uint32_t flag,
+ dbus_bool_t value)
+{
+ unsigned char *flags_p;
+
+ flags_p = _dbus_string_get_udata_len (&header->data, FLAGS_OFFSET, 1);
+
+ if (value)
+ *flags_p |= flag;
+ else
+ *flags_p &= ~flag;
+}
+
+/**
+ * Gets a message flag bit, returning TRUE if the bit is set.
+ *
+ * @param header the header
+ * @param flag the message flag to get
+ * @returns #TRUE if the flag is set
+ */
+dbus_bool_t
+_dbus_header_get_flag (DBusHeader *header,
+ dbus_uint32_t flag)
+{
+ const unsigned char *flags_p;
+
+ flags_p = _dbus_string_get_const_udata_len (&header->data, FLAGS_OFFSET, 1);
+
+ return (*flags_p & flag) != 0;
+}
+
+/**
+ * Swaps the header into the given order if required.
+ *
+ * @param header the header
+ * @param new_order the new byte order
+ */
+void
+_dbus_header_byteswap (DBusHeader *header,
+ int new_order)
+{
+ char byte_order;
+
+ byte_order = _dbus_header_get_byte_order (header);
+
+ if (byte_order == new_order)
+ return;
+
+ _dbus_marshal_byteswap (&_dbus_header_signature_str,
+ 0, byte_order,
+ new_order,
+ &header->data, 0);
+
+ _dbus_string_set_byte (&header->data, BYTE_ORDER_OFFSET, new_order);
+}
+
+/**
+ * Remove every header field not known to this version of dbus.
+ *
+ * @param header the header
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_header_remove_unknown_fields (DBusHeader *header)
+{
+ DBusTypeReader array;
+ DBusTypeReader fields_reader;
+
+ _dbus_type_reader_init (&fields_reader,
+ _dbus_header_get_byte_order (header),
+ &_dbus_header_signature_str,
+ FIELDS_ARRAY_SIGNATURE_OFFSET,
+ &header->data,
+ FIELDS_ARRAY_LENGTH_OFFSET);
+
+ _dbus_type_reader_recurse (&fields_reader, &array);
+
+ while (_dbus_type_reader_get_current_type (&array) != DBUS_TYPE_INVALID)
+ {
+ DBusTypeReader sub;
+ unsigned char field_code;
+
+ _dbus_type_reader_recurse (&array, &sub);
+
+ _dbus_assert (_dbus_type_reader_get_current_type (&sub) == DBUS_TYPE_BYTE);
+ _dbus_type_reader_read_basic (&sub, &field_code);
+
+ if (field_code > DBUS_HEADER_FIELD_LAST)
+ {
+ if (!reserve_header_padding (header))
+ return FALSE;
+
+ if (!_dbus_type_reader_delete (&array, &fields_reader))
+ return FALSE;
+
+ correct_header_padding (header);
+ _dbus_header_cache_invalidate_all (header);
+ }
+ else
+ {
+ _dbus_type_reader_next (&array);
+ }
+ }
+
+ return TRUE;
+}
+
+/** @} */
diff --git a/src/3rdparty/libdbus/dbus/dbus-marshal-header.h b/src/3rdparty/libdbus/dbus/dbus-marshal-header.h
new file mode 100644
index 00000000..c59033ad
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-marshal-header.h
@@ -0,0 +1,179 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-marshal-header.h Managing marshaling/demarshaling of message headers
+ *
+ * Copyright (C) 2005 Red Hat, Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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
+ *
+ */
+
+#ifndef DBUS_MARSHAL_HEADER_H
+#define DBUS_MARSHAL_HEADER_H
+
+#include <dbus/dbus-marshal-basic.h>
+#include <dbus/dbus-marshal-validate.h>
+
+typedef struct DBusHeader DBusHeader;
+typedef struct DBusHeaderField DBusHeaderField;
+
+#define _DBUS_HEADER_FIELD_VALUE_UNKNOWN -1
+#define _DBUS_HEADER_FIELD_VALUE_NONEXISTENT -2
+
+/**
+ * Cached information about a header field in the message
+ */
+struct DBusHeaderField
+{
+ int value_pos; /**< Position of field value, or -1/-2 */
+};
+
+/**
+ * Message header data and some cached details of it.
+ *
+ * A message looks like this:
+ *
+ * @code
+ * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | <- index % 8
+ * |-------|-------|-------|------|-----|-----|-----|-----|
+ * | Order | Type | Flags | Vers | Body length |
+ * | Serial | Fields array length [A]
+ * [A] Code |Sig.len| Signature + \0 | Content...| <- first field
+ * | Content ... | Pad to 8-byte boundary|
+ * | Code |Sig.len| Signature + \0 | Content... | <- second field
+ * ...
+ * | Code |Sig.len| Signature | Content... | <- last field
+ * | Content ... [B] Padding to 8-byte boundary [C]
+ * [C] Body ... |
+ * ...
+ * | Body ... [D] <- no padding after natural length
+ * @endcode
+ *
+ * Each field is a struct<byte,variant>. All structs have 8-byte alignment,
+ * so each field is preceded by 0-7 bytes of padding to an 8-byte boundary
+ * (for the first field it happens to be 0 bytes). The overall header
+ * is followed by 0-7 bytes of padding to align the body.
+ *
+ * Key to content, with variable name references for _dbus_header_load():
+ *
+ * Order: byte order, currently 'l' or 'B' (byte_order)
+ * Type: message type such as DBUS_MESSAGE_TYPE_METHOD_CALL
+ * Flags: message flags such as DBUS_HEADER_FLAG_NO_REPLY_EXPECTED
+ * Vers: D-Bus wire protocol version, currently always 1
+ * Body length: Distance from [C] to [D]
+ * Serial: Message serial number
+ * Fields array length: Distance from [A] to [B] (fields_array_len)
+ *
+ * To understand _dbus_header_load():
+ *
+ * [A] is FIRST_FIELD_OFFSET.
+ * header_len is from 0 to [C].
+ * padding_start is [B].
+ * padding_len is the padding from [B] to [C].
+ */
+struct DBusHeader
+{
+ DBusString data; /**< Header network data, stored
+ * separately from body so we can
+ * independently realloc it. Its length includes
+ * up to 8 bytes of padding to align the body to
+ * an 8-byte boundary.
+ *
+ * In a steady state, this has length [C]. During
+ * editing, it is temporarily extended to have the
+ * maximum possible padding.
+ */
+
+ DBusHeaderField fields[DBUS_HEADER_FIELD_LAST + 1]; /**< Track the location
+ * of each field in header
+ */
+
+ dbus_uint32_t padding : 3; /**< 0-7 bytes of alignment in header,
+ the distance from [B] to [C] */
+ dbus_uint32_t byte_order : 8; /**< byte order of header (must always
+ match the content of byte 0) */
+};
+
+dbus_bool_t _dbus_header_init (DBusHeader *header);
+void _dbus_header_free (DBusHeader *header);
+void _dbus_header_reinit (DBusHeader *header);
+dbus_bool_t _dbus_header_create (DBusHeader *header,
+ int byte_order,
+ int type,
+ const char *destination,
+ const char *path,
+ const char *interface,
+ const char *member,
+ const char *error_name);
+dbus_bool_t _dbus_header_copy (const DBusHeader *header,
+ DBusHeader *dest);
+int _dbus_header_get_message_type (DBusHeader *header);
+void _dbus_header_set_serial (DBusHeader *header,
+ dbus_uint32_t serial);
+dbus_uint32_t _dbus_header_get_serial (DBusHeader *header);
+void _dbus_header_update_lengths (DBusHeader *header,
+ int body_len);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_header_set_field_basic (DBusHeader *header,
+ int field,
+ int type,
+ const void *value);
+dbus_bool_t _dbus_header_get_field_basic (DBusHeader *header,
+ int field,
+ int type,
+ void *value);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_header_get_field_raw (DBusHeader *header,
+ int field,
+ const DBusString **str,
+ int *pos);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_header_delete_field (DBusHeader *header,
+ int field);
+void _dbus_header_toggle_flag (DBusHeader *header,
+ dbus_uint32_t flag,
+ dbus_bool_t value);
+dbus_bool_t _dbus_header_get_flag (DBusHeader *header,
+ dbus_uint32_t flag);
+dbus_bool_t _dbus_header_ensure_signature (DBusHeader *header,
+ DBusString **type_str,
+ int *type_pos);
+dbus_bool_t _dbus_header_have_message_untrusted (int max_message_length,
+ DBusValidity *validity,
+ int *byte_order,
+ int *fields_array_len,
+ int *header_len,
+ int *body_len,
+ const DBusString *str,
+ int start,
+ int len);
+dbus_bool_t _dbus_header_load (DBusHeader *header,
+ DBusValidationMode mode,
+ DBusValidity *validity,
+ int byte_order,
+ int fields_array_len,
+ int header_len,
+ int body_len,
+ const DBusString *str);
+void _dbus_header_byteswap (DBusHeader *header,
+ int new_order);
+DBUS_PRIVATE_EXPORT
+char _dbus_header_get_byte_order (const DBusHeader *header);
+dbus_bool_t _dbus_header_remove_unknown_fields (DBusHeader *header);
+
+#endif /* DBUS_MARSHAL_HEADER_H */
diff --git a/src/3rdparty/libdbus/dbus/dbus-marshal-recursive.c b/src/3rdparty/libdbus/dbus/dbus-marshal-recursive.c
new file mode 100644
index 00000000..a85aabd8
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-marshal-recursive.c
@@ -0,0 +1,2754 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-marshal-recursive.c Marshalling routines for recursive types
+ *
+ * Copyright (C) 2004, 2005 Red Hat, Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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-marshal-recursive.h"
+#include "dbus-marshal-basic.h"
+#include "dbus-signature.h"
+#include "dbus-internals.h"
+
+/**
+ * @addtogroup DBusMarshal
+ * @{
+ */
+
+static dbus_bool_t _dbus_type_reader_greater_than (const DBusTypeReader *lhs,
+ const DBusTypeReader *rhs);
+
+static void _dbus_type_writer_set_enabled (DBusTypeWriter *writer,
+ dbus_bool_t enabled);
+static dbus_bool_t _dbus_type_writer_write_reader_partial (DBusTypeWriter *writer,
+ DBusTypeReader *reader,
+ const DBusTypeReader *start_after,
+ int start_after_new_pos,
+ int start_after_new_len,
+ DBusList **fixups);
+
+/** turn this on to get deluged in TypeReader verbose spam */
+#define RECURSIVE_MARSHAL_READ_TRACE 0
+
+/** turn this on to get deluged in TypeWriter verbose spam */
+#define RECURSIVE_MARSHAL_WRITE_TRACE 0
+
+static void
+free_fixups (DBusList **fixups)
+{
+ DBusList *link;
+
+ link = _dbus_list_get_first_link (fixups);
+ while (link != NULL)
+ {
+ DBusList *next;
+
+ next = _dbus_list_get_next_link (fixups, link);
+
+ dbus_free (link->data);
+ _dbus_list_free_link (link);
+
+ link = next;
+ }
+
+ *fixups = NULL;
+}
+
+static void
+apply_and_free_fixups (DBusList **fixups,
+ DBusTypeReader *reader)
+{
+ DBusList *link;
+
+#if RECURSIVE_MARSHAL_WRITE_TRACE
+ if (*fixups)
+ _dbus_verbose (" %d FIXUPS to apply\n",
+ _dbus_list_get_length (fixups));
+#endif
+
+ link = _dbus_list_get_first_link (fixups);
+ while (link != NULL)
+ {
+ DBusList *next;
+
+ next = _dbus_list_get_next_link (fixups, link);
+
+ if (reader)
+ {
+ DBusArrayLenFixup *f;
+
+ f = link->data;
+
+#if RECURSIVE_MARSHAL_WRITE_TRACE
+ _dbus_verbose (" applying FIXUP to reader %p at pos %d new_len = %d old len %d\n",
+ reader, f->len_pos_in_reader, f->new_len,
+ _dbus_marshal_read_uint32 (reader->value_str,
+ f->len_pos_in_reader,
+ reader->byte_order, NULL));
+#endif
+
+ _dbus_marshal_set_uint32 ((DBusString*) reader->value_str,
+ f->len_pos_in_reader,
+ f->new_len,
+ reader->byte_order);
+ }
+
+ dbus_free (link->data);
+ _dbus_list_free_link (link);
+
+ link = next;
+ }
+
+ *fixups = NULL;
+}
+
+/**
+ * Virtual table for a type reader.
+ */
+struct DBusTypeReaderClass
+{
+ const char *name; /**< name for debugging */
+ int id; /**< index in all_reader_classes */
+ dbus_bool_t types_only; /**< only iterates over types, not values */
+ void (* recurse) (DBusTypeReader *sub,
+ DBusTypeReader *parent); /**< recurse with this reader as sub */
+ dbus_bool_t (* check_finished) (const DBusTypeReader *reader); /**< check whether reader is at the end */
+ void (* next) (DBusTypeReader *reader,
+ int current_type); /**< go to the next value */
+};
+
+static int
+element_type_get_alignment (const DBusString *str,
+ int pos)
+{
+ return _dbus_type_get_alignment (_dbus_first_type_in_signature (str, pos));
+}
+
+static void
+reader_init (DBusTypeReader *reader,
+ int byte_order,
+ const DBusString *type_str,
+ int type_pos,
+ const DBusString *value_str,
+ int value_pos)
+{
+ _DBUS_ZERO (*reader);
+ reader->byte_order = byte_order;
+ reader->finished = FALSE;
+ reader->type_str = type_str;
+ reader->type_pos = type_pos;
+ reader->value_str = value_str;
+ reader->value_pos = value_pos;
+}
+
+static void
+base_reader_recurse (DBusTypeReader *sub,
+ DBusTypeReader *parent)
+{
+ /* point subreader at the same place as parent */
+ reader_init (sub,
+ parent->byte_order,
+ parent->type_str,
+ parent->type_pos,
+ parent->value_str,
+ parent->value_pos);
+}
+
+static void
+struct_or_dict_entry_types_only_reader_recurse (DBusTypeReader *sub,
+ DBusTypeReader *parent)
+{
+ base_reader_recurse (sub, parent);
+
+ _dbus_assert (_dbus_string_get_byte (sub->type_str,
+ sub->type_pos) == DBUS_STRUCT_BEGIN_CHAR ||
+ _dbus_string_get_byte (sub->type_str,
+ sub->type_pos) == DBUS_DICT_ENTRY_BEGIN_CHAR);
+
+ sub->type_pos += 1;
+}
+
+static void
+struct_or_dict_entry_reader_recurse (DBusTypeReader *sub,
+ DBusTypeReader *parent)
+{
+ struct_or_dict_entry_types_only_reader_recurse (sub, parent);
+
+ /* struct and dict entry have 8 byte alignment */
+ sub->value_pos = _DBUS_ALIGN_VALUE (sub->value_pos, 8);
+}
+
+static void
+array_types_only_reader_recurse (DBusTypeReader *sub,
+ DBusTypeReader *parent)
+{
+ base_reader_recurse (sub, parent);
+
+ /* point type_pos at the array element type */
+ sub->type_pos += 1;
+
+ /* Init with values likely to crash things if misused */
+ sub->u.array.start_pos = _DBUS_INT_MAX;
+ sub->array_len_offset = 7;
+}
+
+/** compute position of array length given array_len_offset, which is
+ the offset back from start_pos to end of the len */
+#define ARRAY_READER_LEN_POS(reader) \
+ ((reader)->u.array.start_pos - ((int)(reader)->array_len_offset) - 4)
+
+static int
+array_reader_get_array_len (const DBusTypeReader *reader)
+{
+ dbus_uint32_t array_len;
+ int len_pos;
+
+ len_pos = ARRAY_READER_LEN_POS (reader);
+
+ _dbus_assert (_DBUS_ALIGN_VALUE (len_pos, 4) == (unsigned) len_pos);
+ array_len = _dbus_unpack_uint32 (reader->byte_order,
+ _dbus_string_get_const_udata_len (reader->value_str, len_pos, 4));
+
+#if RECURSIVE_MARSHAL_READ_TRACE
+ _dbus_verbose (" reader %p len_pos %d array len %u len_offset %d\n",
+ reader, len_pos, array_len, reader->array_len_offset);
+#endif
+
+ _dbus_assert (reader->u.array.start_pos - len_pos - 4 < 8);
+
+ return array_len;
+}
+
+static void
+array_reader_recurse (DBusTypeReader *sub,
+ DBusTypeReader *parent)
+{
+ int alignment;
+ int len_pos;
+
+ array_types_only_reader_recurse (sub, parent);
+
+ sub->value_pos = _DBUS_ALIGN_VALUE (sub->value_pos, 4);
+
+ len_pos = sub->value_pos;
+
+ sub->value_pos += 4; /* for the length */
+
+ alignment = element_type_get_alignment (sub->type_str,
+ sub->type_pos);
+
+ sub->value_pos = _DBUS_ALIGN_VALUE (sub->value_pos, alignment);
+
+ sub->u.array.start_pos = sub->value_pos;
+ _dbus_assert ((sub->u.array.start_pos - (len_pos + 4)) < 8); /* only 3 bits in array_len_offset */
+ sub->array_len_offset = sub->u.array.start_pos - (len_pos + 4);
+
+#if RECURSIVE_MARSHAL_READ_TRACE
+ _dbus_verbose (" type reader %p array start = %d len_offset = %d array len = %d array element type = %s\n",
+ sub,
+ sub->u.array.start_pos,
+ sub->array_len_offset,
+ array_reader_get_array_len (sub),
+ _dbus_type_to_string (_dbus_first_type_in_signature (sub->type_str,
+ sub->type_pos)));
+#endif
+}
+
+static void
+variant_reader_recurse (DBusTypeReader *sub,
+ DBusTypeReader *parent)
+{
+ int sig_len;
+ int contained_alignment;
+
+ base_reader_recurse (sub, parent);
+
+ /* Variant is 1 byte sig length (without nul), signature with nul,
+ * padding to 8-boundary, then values
+ */
+
+ sig_len = _dbus_string_get_byte (sub->value_str, sub->value_pos);
+
+ sub->type_str = sub->value_str;
+ sub->type_pos = sub->value_pos + 1;
+
+ sub->value_pos = sub->type_pos + sig_len + 1;
+
+ contained_alignment = _dbus_type_get_alignment (_dbus_first_type_in_signature (sub->type_str,
+ sub->type_pos));
+
+ sub->value_pos = _DBUS_ALIGN_VALUE (sub->value_pos, contained_alignment);
+
+#if RECURSIVE_MARSHAL_READ_TRACE
+ _dbus_verbose (" type reader %p variant containing '%s'\n",
+ sub,
+ _dbus_string_get_const_data_len (sub->type_str,
+ sub->type_pos, 0));
+#endif
+}
+
+/* return true if no more elements remain */
+static dbus_bool_t
+array_reader_check_finished (const DBusTypeReader *reader)
+{
+ int end_pos;
+
+ end_pos = reader->u.array.start_pos + array_reader_get_array_len (reader);
+
+ _dbus_assert (reader->value_pos <= end_pos);
+ _dbus_assert (reader->value_pos >= reader->u.array.start_pos);
+
+ return reader->value_pos == end_pos;
+}
+
+static void
+skip_one_complete_type (const DBusString *type_str,
+ int *type_pos)
+{
+ _dbus_type_signature_next (_dbus_string_get_const_data (type_str),
+ type_pos);
+}
+
+/**
+ * Skips to the next "complete" type inside a type signature.
+ * The signature is read starting at type_pos, and the next
+ * type position is stored in the same variable.
+ *
+ * @param type_str a type signature (must be valid)
+ * @param type_pos an integer position in the type signature (in and out)
+ */
+void
+_dbus_type_signature_next (const char *type_str,
+ int *type_pos)
+{
+ const unsigned char *p;
+ const unsigned char *start;
+
+ _dbus_assert (type_str != NULL);
+ _dbus_assert (type_pos != NULL);
+
+ start = (const unsigned char *)type_str;
+ p = start + *type_pos;
+
+ _dbus_assert (*p != DBUS_STRUCT_END_CHAR);
+ _dbus_assert (*p != DBUS_DICT_ENTRY_END_CHAR);
+
+ while (*p == DBUS_TYPE_ARRAY)
+ ++p;
+
+ _dbus_assert (*p != DBUS_STRUCT_END_CHAR);
+ _dbus_assert (*p != DBUS_DICT_ENTRY_END_CHAR);
+
+ if (*p == DBUS_STRUCT_BEGIN_CHAR)
+ {
+ int depth;
+
+ depth = 1;
+
+ while (TRUE)
+ {
+ _dbus_assert (*p != DBUS_TYPE_INVALID);
+
+ ++p;
+
+ _dbus_assert (*p != DBUS_TYPE_INVALID);
+
+ if (*p == DBUS_STRUCT_BEGIN_CHAR)
+ depth += 1;
+ else if (*p == DBUS_STRUCT_END_CHAR)
+ {
+ depth -= 1;
+ if (depth == 0)
+ {
+ ++p;
+ break;
+ }
+ }
+ }
+ }
+ else if (*p == DBUS_DICT_ENTRY_BEGIN_CHAR)
+ {
+ int depth;
+
+ depth = 1;
+
+ while (TRUE)
+ {
+ _dbus_assert (*p != DBUS_TYPE_INVALID);
+
+ ++p;
+
+ _dbus_assert (*p != DBUS_TYPE_INVALID);
+
+ if (*p == DBUS_DICT_ENTRY_BEGIN_CHAR)
+ depth += 1;
+ else if (*p == DBUS_DICT_ENTRY_END_CHAR)
+ {
+ depth -= 1;
+ if (depth == 0)
+ {
+ ++p;
+ break;
+ }
+ }
+ }
+ }
+ else
+ {
+ ++p;
+ }
+
+ *type_pos = (int) (p - start);
+}
+
+static int
+find_len_of_complete_type (const DBusString *type_str,
+ int type_pos)
+{
+ int end;
+
+ end = type_pos;
+
+ skip_one_complete_type (type_str, &end);
+
+ return end - type_pos;
+}
+
+static void
+base_reader_next (DBusTypeReader *reader,
+ int current_type)
+{
+ switch (current_type)
+ {
+ case DBUS_TYPE_DICT_ENTRY:
+ case DBUS_TYPE_STRUCT:
+ case DBUS_TYPE_VARIANT:
+ /* Scan forward over the entire container contents */
+ {
+ DBusTypeReader sub;
+
+ if (reader->klass->types_only && current_type == DBUS_TYPE_VARIANT)
+ ;
+ else
+ {
+ /* Recurse into the struct or variant */
+ _dbus_type_reader_recurse (reader, &sub);
+
+ /* Skip everything in this subreader */
+ while (_dbus_type_reader_next (&sub))
+ {
+ /* nothing */;
+ }
+ }
+ if (!reader->klass->types_only)
+ reader->value_pos = sub.value_pos;
+
+ /* Now we are at the end of this container; for variants, the
+ * subreader's type_pos is totally inapplicable (it's in the
+ * value string) but we know that we increment by one past the
+ * DBUS_TYPE_VARIANT
+ */
+ if (current_type == DBUS_TYPE_VARIANT)
+ reader->type_pos += 1;
+ else
+ reader->type_pos = sub.type_pos;
+ }
+ break;
+
+ case DBUS_TYPE_ARRAY:
+ {
+ if (!reader->klass->types_only)
+ _dbus_marshal_skip_array (reader->value_str,
+ _dbus_first_type_in_signature (reader->type_str,
+ reader->type_pos + 1),
+ reader->byte_order,
+ &reader->value_pos);
+
+ skip_one_complete_type (reader->type_str, &reader->type_pos);
+ }
+ break;
+
+ default:
+ if (!reader->klass->types_only)
+ _dbus_marshal_skip_basic (reader->value_str,
+ current_type, reader->byte_order,
+ &reader->value_pos);
+
+ reader->type_pos += 1;
+ break;
+ }
+}
+
+static void
+struct_reader_next (DBusTypeReader *reader,
+ int current_type)
+{
+ int t;
+
+ base_reader_next (reader, current_type);
+
+ /* for STRUCT containers we return FALSE at the end of the struct,
+ * for INVALID we return FALSE at the end of the signature.
+ * In both cases we arrange for get_current_type() to return INVALID
+ * which is defined to happen iff we're at the end (no more next())
+ */
+ t = _dbus_string_get_byte (reader->type_str, reader->type_pos);
+ if (t == DBUS_STRUCT_END_CHAR)
+ {
+ reader->type_pos += 1;
+ reader->finished = TRUE;
+ }
+}
+
+static void
+dict_entry_reader_next (DBusTypeReader *reader,
+ int current_type)
+{
+ int t;
+
+ base_reader_next (reader, current_type);
+
+ /* for STRUCT containers we return FALSE at the end of the struct,
+ * for INVALID we return FALSE at the end of the signature.
+ * In both cases we arrange for get_current_type() to return INVALID
+ * which is defined to happen iff we're at the end (no more next())
+ */
+ t = _dbus_string_get_byte (reader->type_str, reader->type_pos);
+ if (t == DBUS_DICT_ENTRY_END_CHAR)
+ {
+ reader->type_pos += 1;
+ reader->finished = TRUE;
+ }
+}
+
+static void
+array_types_only_reader_next (DBusTypeReader *reader,
+ int current_type)
+{
+ /* We have one "element" to be iterated over
+ * in each array, which is its element type.
+ * So the finished flag indicates whether we've
+ * iterated over it yet or not.
+ */
+ reader->finished = TRUE;
+}
+
+static void
+array_reader_next (DBusTypeReader *reader,
+ int current_type)
+{
+ /* Skip one array element */
+ int end_pos;
+
+ end_pos = reader->u.array.start_pos + array_reader_get_array_len (reader);
+
+#if RECURSIVE_MARSHAL_READ_TRACE
+ _dbus_verbose (" reader %p array next START start_pos = %d end_pos = %d value_pos = %d current_type = %s\n",
+ reader,
+ reader->u.array.start_pos,
+ end_pos, reader->value_pos,
+ _dbus_type_to_string (current_type));
+#endif
+
+ _dbus_assert (reader->value_pos < end_pos);
+ _dbus_assert (reader->value_pos >= reader->u.array.start_pos);
+
+ switch (_dbus_first_type_in_signature (reader->type_str,
+ reader->type_pos))
+ {
+ case DBUS_TYPE_DICT_ENTRY:
+ case DBUS_TYPE_STRUCT:
+ case DBUS_TYPE_VARIANT:
+ {
+ DBusTypeReader sub;
+
+ /* Recurse into the struct or variant */
+ _dbus_type_reader_recurse (reader, &sub);
+
+ /* Skip everything in this element */
+ while (_dbus_type_reader_next (&sub))
+ {
+ /* nothing */;
+ }
+
+ /* Now we are at the end of this element */
+ reader->value_pos = sub.value_pos;
+ }
+ break;
+
+ case DBUS_TYPE_ARRAY:
+ {
+ _dbus_marshal_skip_array (reader->value_str,
+ _dbus_first_type_in_signature (reader->type_str,
+ reader->type_pos + 1),
+ reader->byte_order,
+ &reader->value_pos);
+ }
+ break;
+
+ default:
+ {
+ _dbus_marshal_skip_basic (reader->value_str,
+ current_type, reader->byte_order,
+ &reader->value_pos);
+ }
+ break;
+ }
+
+#if RECURSIVE_MARSHAL_READ_TRACE
+ _dbus_verbose (" reader %p array next END start_pos = %d end_pos = %d value_pos = %d current_type = %s\n",
+ reader,
+ reader->u.array.start_pos,
+ end_pos, reader->value_pos,
+ _dbus_type_to_string (current_type));
+#endif
+
+ _dbus_assert (reader->value_pos <= end_pos);
+
+ if (reader->value_pos == end_pos)
+ {
+ skip_one_complete_type (reader->type_str,
+ &reader->type_pos);
+ }
+}
+
+static const DBusTypeReaderClass body_reader_class = {
+ "body", 0,
+ FALSE,
+ NULL, /* body is always toplevel, so doesn't get recursed into */
+ NULL,
+ base_reader_next
+};
+
+static const DBusTypeReaderClass body_types_only_reader_class = {
+ "body types", 1,
+ TRUE,
+ NULL, /* body is always toplevel, so doesn't get recursed into */
+ NULL,
+ base_reader_next
+};
+
+static const DBusTypeReaderClass struct_reader_class = {
+ "struct", 2,
+ FALSE,
+ struct_or_dict_entry_reader_recurse,
+ NULL,
+ struct_reader_next
+};
+
+static const DBusTypeReaderClass struct_types_only_reader_class = {
+ "struct types", 3,
+ TRUE,
+ struct_or_dict_entry_types_only_reader_recurse,
+ NULL,
+ struct_reader_next
+};
+
+static const DBusTypeReaderClass dict_entry_reader_class = {
+ "dict_entry", 4,
+ FALSE,
+ struct_or_dict_entry_reader_recurse,
+ NULL,
+ dict_entry_reader_next
+};
+
+static const DBusTypeReaderClass dict_entry_types_only_reader_class = {
+ "dict_entry types", 5,
+ TRUE,
+ struct_or_dict_entry_types_only_reader_recurse,
+ NULL,
+ dict_entry_reader_next
+};
+
+static const DBusTypeReaderClass array_reader_class = {
+ "array", 6,
+ FALSE,
+ array_reader_recurse,
+ array_reader_check_finished,
+ array_reader_next
+};
+
+static const DBusTypeReaderClass array_types_only_reader_class = {
+ "array types", 7,
+ TRUE,
+ array_types_only_reader_recurse,
+ NULL,
+ array_types_only_reader_next
+};
+
+static const DBusTypeReaderClass variant_reader_class = {
+ "variant", 8,
+ FALSE,
+ variant_reader_recurse,
+ NULL,
+ base_reader_next
+};
+
+#ifndef DBUS_DISABLE_ASSERT
+static const DBusTypeReaderClass * const
+all_reader_classes[] = {
+ &body_reader_class,
+ &body_types_only_reader_class,
+ &struct_reader_class,
+ &struct_types_only_reader_class,
+ &dict_entry_reader_class,
+ &dict_entry_types_only_reader_class,
+ &array_reader_class,
+ &array_types_only_reader_class,
+ &variant_reader_class
+};
+#endif
+
+/**
+ * Initializes a type reader.
+ *
+ * @param reader the reader
+ * @param byte_order the byte order of the block to read
+ * @param type_str the signature of the block to read
+ * @param type_pos location of signature
+ * @param value_str the string containing values block
+ * @param value_pos start of values block
+ */
+void
+_dbus_type_reader_init (DBusTypeReader *reader,
+ int byte_order,
+ const DBusString *type_str,
+ int type_pos,
+ const DBusString *value_str,
+ int value_pos)
+{
+ reader_init (reader, byte_order, type_str, type_pos,
+ value_str, value_pos);
+
+ reader->klass = &body_reader_class;
+
+#if RECURSIVE_MARSHAL_READ_TRACE
+ _dbus_verbose (" type reader %p init type_pos = %d value_pos = %d remaining sig '%s'\n",
+ reader, reader->type_pos, reader->value_pos,
+ _dbus_string_get_const_data_len (reader->type_str, reader->type_pos, 0));
+#endif
+}
+
+/**
+ * Like _dbus_type_reader_init() but the iteration is over the
+ * signature, not over values.
+ *
+ * @param reader the reader
+ * @param type_str the signature string
+ * @param type_pos location in the signature string
+ */
+void
+_dbus_type_reader_init_types_only (DBusTypeReader *reader,
+ const DBusString *type_str,
+ int type_pos)
+{
+ reader_init (reader, DBUS_COMPILER_BYTE_ORDER /* irrelevant */,
+ type_str, type_pos, NULL, _DBUS_INT_MAX /* crashes if we screw up */);
+
+ reader->klass = &body_types_only_reader_class;
+
+#if RECURSIVE_MARSHAL_READ_TRACE
+ _dbus_verbose (" type reader %p init types only type_pos = %d remaining sig '%s'\n",
+ reader, reader->type_pos,
+ _dbus_string_get_const_data_len (reader->type_str, reader->type_pos, 0));
+#endif
+}
+
+/**
+ * Gets the type of the value the reader is currently pointing to;
+ * or for a types-only reader gets the type it's currently pointing to.
+ * If the reader is at the end of a block or end of a container such
+ * as an array, returns #DBUS_TYPE_INVALID.
+ *
+ * @param reader the reader
+ */
+int
+_dbus_type_reader_get_current_type (const DBusTypeReader *reader)
+{
+ int t;
+
+ if (reader->finished ||
+ (reader->klass->check_finished &&
+ (* reader->klass->check_finished) (reader)))
+ t = DBUS_TYPE_INVALID;
+ else
+ t = _dbus_first_type_in_signature (reader->type_str,
+ reader->type_pos);
+
+ _dbus_assert (t != DBUS_STRUCT_END_CHAR);
+ _dbus_assert (t != DBUS_STRUCT_BEGIN_CHAR);
+ _dbus_assert (t != DBUS_DICT_ENTRY_END_CHAR);
+ _dbus_assert (t != DBUS_DICT_ENTRY_BEGIN_CHAR);
+
+#if 0
+ _dbus_verbose (" type reader %p current type_pos = %d type = %s\n",
+ reader, reader->type_pos,
+ _dbus_type_to_string (t));
+#endif
+
+ return t;
+}
+
+/**
+ * Gets the type of an element of the array the reader is currently
+ * pointing to. It's an error to call this if
+ * _dbus_type_reader_get_current_type() doesn't return #DBUS_TYPE_ARRAY
+ * for this reader.
+ *
+ * @param reader the reader
+ */
+int
+_dbus_type_reader_get_element_type (const DBusTypeReader *reader)
+{
+ int element_type;
+
+ _dbus_assert (_dbus_type_reader_get_current_type (reader) == DBUS_TYPE_ARRAY);
+
+ element_type = _dbus_first_type_in_signature (reader->type_str,
+ reader->type_pos + 1);
+
+ return element_type;
+}
+
+/**
+ * Gets the current position in the value block
+ * @param reader the reader
+ */
+int
+_dbus_type_reader_get_value_pos (const DBusTypeReader *reader)
+{
+ return reader->value_pos;
+}
+
+/**
+ * Get the address of the marshaled value in the data being read. The
+ * address may not be aligned; you have to align it to the type of the
+ * value you want to read. Most of the demarshal routines do this for
+ * you.
+ *
+ * @param reader the reader
+ * @param value_location the address of the marshaled value
+ */
+void
+_dbus_type_reader_read_raw (const DBusTypeReader *reader,
+ const unsigned char **value_location)
+{
+ _dbus_assert (!reader->klass->types_only);
+
+ *value_location = _dbus_string_get_const_udata_len (reader->value_str,
+ reader->value_pos,
+ 0);
+}
+
+/**
+ * Reads a basic-typed value, as with _dbus_marshal_read_basic().
+ *
+ * @param reader the reader
+ * @param value the address of the value
+ */
+void
+_dbus_type_reader_read_basic (const DBusTypeReader *reader,
+ void *value)
+{
+ int t;
+
+ _dbus_assert (!reader->klass->types_only);
+
+ t = _dbus_type_reader_get_current_type (reader);
+
+ _dbus_marshal_read_basic (reader->value_str,
+ reader->value_pos,
+ t, value,
+ reader->byte_order,
+ NULL);
+
+
+#if RECURSIVE_MARSHAL_READ_TRACE
+ _dbus_verbose (" type reader %p read basic type_pos = %d value_pos = %d remaining sig '%s'\n",
+ reader, reader->type_pos, reader->value_pos,
+ _dbus_string_get_const_data_len (reader->type_str, reader->type_pos, 0));
+#endif
+}
+
+/**
+ * Returns the number of bytes in the array.
+ *
+ * @param reader the reader to read from
+ * @returns the number of bytes in the array
+ */
+int
+_dbus_type_reader_get_array_length (const DBusTypeReader *reader)
+{
+ _dbus_assert (!reader->klass->types_only);
+ _dbus_assert (reader->klass == &array_reader_class);
+
+ return array_reader_get_array_len (reader);
+}
+
+/**
+ * Reads a block of fixed-length basic values, from the current point
+ * in an array to the end of the array. Does not work for arrays of
+ * string or container types.
+ *
+ * This function returns the array in-place; it does not make a copy,
+ * and it does not swap the bytes.
+ *
+ * If you ask for #DBUS_TYPE_DOUBLE you will get a "const double*" back
+ * and the "value" argument should be a "const double**" and so on.
+ *
+ * @param reader the reader to read from
+ * @param value place to return the array values
+ * @param n_elements place to return number of array elements
+ */
+void
+_dbus_type_reader_read_fixed_multi (const DBusTypeReader *reader,
+ const void **value,
+ int *n_elements)
+{
+ int element_type;
+ int end_pos;
+ int remaining_len;
+ int alignment;
+ int total_len;
+
+ _dbus_assert (!reader->klass->types_only);
+ _dbus_assert (reader->klass == &array_reader_class);
+
+ element_type = _dbus_first_type_in_signature (reader->type_str,
+ reader->type_pos);
+
+ _dbus_assert (element_type != DBUS_TYPE_INVALID); /* why we don't use get_current_type() */
+ _dbus_assert (dbus_type_is_fixed (element_type));
+
+ alignment = _dbus_type_get_alignment (element_type);
+
+ _dbus_assert (reader->value_pos >= reader->u.array.start_pos);
+
+ total_len = array_reader_get_array_len (reader);
+ end_pos = reader->u.array.start_pos + total_len;
+ remaining_len = end_pos - reader->value_pos;
+
+#if RECURSIVE_MARSHAL_READ_TRACE
+ _dbus_verbose ("end_pos %d total_len %d remaining_len %d value_pos %d\n",
+ end_pos, total_len, remaining_len, reader->value_pos);
+#endif
+
+ _dbus_assert (remaining_len <= total_len);
+
+ if (remaining_len == 0)
+ *value = NULL;
+ else
+ *value = _dbus_string_get_const_data_len (reader->value_str,
+ reader->value_pos,
+ remaining_len);
+
+ *n_elements = remaining_len / alignment;
+ _dbus_assert ((remaining_len % alignment) == 0);
+
+#if RECURSIVE_MARSHAL_READ_TRACE
+ _dbus_verbose (" type reader %p read fixed array type_pos = %d value_pos = %d remaining sig '%s'\n",
+ reader, reader->type_pos, reader->value_pos,
+ _dbus_string_get_const_data_len (reader->type_str, reader->type_pos, 0));
+#endif
+}
+
+/**
+ * Initialize a new reader pointing to the first type and
+ * corresponding value that's a child of the current container. It's
+ * an error to call this if the current type is a non-container.
+ *
+ * Note that DBusTypeReader traverses values, not types. So if you
+ * have an empty array of array of int, you can't recurse into it. You
+ * can only recurse into each element.
+ *
+ * @param reader the reader
+ * @param sub a reader to init pointing to the first child
+ */
+void
+_dbus_type_reader_recurse (DBusTypeReader *reader,
+ DBusTypeReader *sub)
+{
+ int t;
+ const DBusTypeReaderClass *klass = NULL;
+
+ t = _dbus_first_type_in_signature (reader->type_str, reader->type_pos);
+
+ switch (t)
+ {
+ case DBUS_TYPE_STRUCT:
+ if (reader->klass->types_only)
+ klass = &struct_types_only_reader_class;
+ else
+ klass = &struct_reader_class;
+ break;
+ case DBUS_TYPE_DICT_ENTRY:
+ if (reader->klass->types_only)
+ klass = &dict_entry_types_only_reader_class;
+ else
+ klass = &dict_entry_reader_class;
+ break;
+ case DBUS_TYPE_ARRAY:
+ if (reader->klass->types_only)
+ klass = &array_types_only_reader_class;
+ else
+ klass = &array_reader_class;
+ break;
+ case DBUS_TYPE_VARIANT:
+ if (reader->klass->types_only)
+ _dbus_assert_not_reached ("can't recurse into variant typecode");
+ else
+ klass = &variant_reader_class;
+ break;
+ default:
+ _dbus_verbose ("recursing into type %s\n", _dbus_type_to_string (t));
+#ifndef DBUS_DISABLE_CHECKS
+ if (t == DBUS_TYPE_INVALID)
+ _dbus_warn_check_failed ("You can't recurse into an empty array or off the end of a message body");
+#endif /* DBUS_DISABLE_CHECKS */
+
+ _dbus_assert_not_reached ("don't yet handle recursing into this type");
+ }
+
+ _dbus_assert (klass != NULL);
+ _dbus_assert (klass == all_reader_classes[klass->id]);
+
+ (* klass->recurse) (sub, reader);
+ sub->klass = klass;
+
+#if RECURSIVE_MARSHAL_READ_TRACE
+ _dbus_verbose (" type reader %p RECURSED type_pos = %d value_pos = %d remaining sig '%s'\n",
+ sub, sub->type_pos, sub->value_pos,
+ _dbus_string_get_const_data_len (sub->type_str, sub->type_pos, 0));
+#endif
+}
+
+/**
+ * Skip to the next value on this "level". e.g. the next field in a
+ * struct, the next value in an array. Returns FALSE at the end of the
+ * current container.
+ *
+ * @param reader the reader
+ * @returns FALSE if nothing more to read at or below this level
+ */
+dbus_bool_t
+_dbus_type_reader_next (DBusTypeReader *reader)
+{
+ int t;
+
+ t = _dbus_type_reader_get_current_type (reader);
+
+#if RECURSIVE_MARSHAL_READ_TRACE
+ _dbus_verbose (" type reader %p START next() { type_pos = %d value_pos = %d remaining sig '%s' current_type = %s\n",
+ reader, reader->type_pos, reader->value_pos,
+ _dbus_string_get_const_data_len (reader->type_str, reader->type_pos, 0),
+ _dbus_type_to_string (t));
+#endif
+
+ if (t == DBUS_TYPE_INVALID)
+ return FALSE;
+
+ (* reader->klass->next) (reader, t);
+
+#if RECURSIVE_MARSHAL_READ_TRACE
+ _dbus_verbose (" type reader %p END next() type_pos = %d value_pos = %d remaining sig '%s' current_type = %s\n",
+ reader, reader->type_pos, reader->value_pos,
+ _dbus_string_get_const_data_len (reader->type_str, reader->type_pos, 0),
+ _dbus_type_to_string (_dbus_type_reader_get_current_type (reader)));
+#endif
+
+ return _dbus_type_reader_get_current_type (reader) != DBUS_TYPE_INVALID;
+}
+
+/**
+ * Check whether there's another value on this "level". e.g. the next
+ * field in a struct, the next value in an array. Returns FALSE at the
+ * end of the current container.
+ *
+ * You probably don't want to use this; it makes for an awkward for/while
+ * loop. A nicer one is "while ((current_type = get_current_type()) != INVALID)"
+ *
+ * @param reader the reader
+ * @returns FALSE if nothing more to read at or below this level
+ */
+dbus_bool_t
+_dbus_type_reader_has_next (const DBusTypeReader *reader)
+{
+ /* Not efficient but works for now. */
+ DBusTypeReader copy;
+
+ copy = *reader;
+ return _dbus_type_reader_next (&copy);
+}
+
+/**
+ * Gets the string and range of said string containing the signature
+ * of the current value. Essentially a more complete version of
+ * _dbus_type_reader_get_current_type() (returns the full type
+ * rather than only the outside of the onion).
+ *
+ * Note though that the first byte in a struct signature is
+ * #DBUS_STRUCT_BEGIN_CHAR while the current type will be
+ * #DBUS_TYPE_STRUCT so it isn't true that the first byte of the
+ * signature is always the same as the current type. Another
+ * difference is that this function will still return a signature when
+ * inside an empty array; say you recurse into empty array of int32,
+ * the signature is "i" but the current type will always be
+ * #DBUS_TYPE_INVALID since there are no elements to be currently
+ * pointing to.
+ *
+ * @param reader the reader
+ * @param str_p place to return the string with the type in it
+ * @param start_p place to return start of the type
+ * @param len_p place to return the length of the type
+ */
+void
+_dbus_type_reader_get_signature (const DBusTypeReader *reader,
+ const DBusString **str_p,
+ int *start_p,
+ int *len_p)
+{
+ *str_p = reader->type_str;
+ *start_p = reader->type_pos;
+ *len_p = find_len_of_complete_type (reader->type_str, reader->type_pos);
+}
+
+typedef struct
+{
+ DBusString replacement; /**< Marshaled value including alignment padding */
+ int padding; /**< How much of the replacement block is padding */
+} ReplacementBlock;
+
+static dbus_bool_t
+replacement_block_init (ReplacementBlock *block,
+ DBusTypeReader *reader)
+{
+ if (!_dbus_string_init (&block->replacement))
+ return FALSE;
+
+ /* % 8 is the padding to have the same align properties in
+ * our replacement string as we do at the position being replaced
+ */
+ block->padding = reader->value_pos % 8;
+
+ if (!_dbus_string_lengthen (&block->replacement, block->padding))
+ goto oom;
+
+ return TRUE;
+
+ oom:
+ _dbus_string_free (&block->replacement);
+ return FALSE;
+}
+
+static dbus_bool_t
+replacement_block_replace (ReplacementBlock *block,
+ DBusTypeReader *reader,
+ const DBusTypeReader *realign_root)
+{
+ DBusTypeWriter writer;
+ DBusTypeReader realign_reader;
+ DBusList *fixups;
+ int orig_len;
+
+ _dbus_assert (realign_root != NULL);
+
+ orig_len = _dbus_string_get_length (&block->replacement);
+
+ realign_reader = *realign_root;
+
+#if RECURSIVE_MARSHAL_WRITE_TRACE
+ _dbus_verbose ("INITIALIZING replacement block writer %p at value_pos %d\n",
+ &writer, _dbus_string_get_length (&block->replacement));
+#endif
+ _dbus_type_writer_init_values_only (&writer,
+ realign_reader.byte_order,
+ realign_reader.type_str,
+ realign_reader.type_pos,
+ &block->replacement,
+ _dbus_string_get_length (&block->replacement));
+
+ _dbus_assert (realign_reader.value_pos <= reader->value_pos);
+
+#if RECURSIVE_MARSHAL_WRITE_TRACE
+ _dbus_verbose ("COPYING from reader at value_pos %d to writer %p starting after value_pos %d\n",
+ realign_reader.value_pos, &writer, reader->value_pos);
+#endif
+ fixups = NULL;
+ if (!_dbus_type_writer_write_reader_partial (&writer,
+ &realign_reader,
+ reader,
+ block->padding,
+ _dbus_string_get_length (&block->replacement) - block->padding,
+ &fixups))
+ goto oom;
+
+#if RECURSIVE_MARSHAL_WRITE_TRACE
+ _dbus_verbose ("REPLACEMENT at padding %d len %d\n", block->padding,
+ _dbus_string_get_length (&block->replacement) - block->padding);
+ _dbus_verbose_bytes_of_string (&block->replacement, block->padding,
+ _dbus_string_get_length (&block->replacement) - block->padding);
+ _dbus_verbose ("TO BE REPLACED at value_pos = %d (align pad %d) len %d realign_reader.value_pos %d\n",
+ reader->value_pos, reader->value_pos % 8,
+ realign_reader.value_pos - reader->value_pos,
+ realign_reader.value_pos);
+ _dbus_verbose_bytes_of_string (reader->value_str,
+ reader->value_pos,
+ realign_reader.value_pos - reader->value_pos);
+#endif
+
+ /* Move the replacement into position
+ * (realign_reader should now be at the end of the block to be replaced)
+ */
+ if (!_dbus_string_replace_len (&block->replacement, block->padding,
+ _dbus_string_get_length (&block->replacement) - block->padding,
+ (DBusString*) reader->value_str,
+ reader->value_pos,
+ realign_reader.value_pos - reader->value_pos))
+ goto oom;
+
+ /* Process our fixups now that we can't have an OOM error */
+ apply_and_free_fixups (&fixups, reader);
+
+ return TRUE;
+
+ oom:
+ _dbus_string_set_length (&block->replacement, orig_len);
+ free_fixups (&fixups);
+ return FALSE;
+}
+
+static void
+replacement_block_free (ReplacementBlock *block)
+{
+ _dbus_string_free (&block->replacement);
+}
+
+/* In the variable-length case, we have to fix alignment after we insert.
+ * The strategy is as follows:
+ *
+ * - pad a new string to have the same alignment as the
+ * start of the current basic value
+ * - write the new basic value
+ * - copy from the original reader to the new string,
+ * which will fix the alignment of types following
+ * the new value
+ * - this copy has to start at realign_root,
+ * but not really write anything until it
+ * passes the value being set
+ * - as an optimization, we can stop copying
+ * when the source and dest values are both
+ * on an 8-boundary, since we know all following
+ * padding and alignment will be identical
+ * - copy the new string back to the original
+ * string, replacing the relevant part of the
+ * original string
+ * - now any arrays in the original string that
+ * contained the replaced string may have the
+ * wrong length; so we have to fix that
+ */
+static dbus_bool_t
+reader_set_basic_variable_length (DBusTypeReader *reader,
+ int current_type,
+ const void *value,
+ const DBusTypeReader *realign_root)
+{
+ dbus_bool_t retval;
+ ReplacementBlock block;
+ DBusTypeWriter writer;
+
+ _dbus_assert (realign_root != NULL);
+
+ retval = FALSE;
+
+ if (!replacement_block_init (&block, reader))
+ return FALSE;
+
+ /* Write the new basic value */
+#if RECURSIVE_MARSHAL_WRITE_TRACE
+ _dbus_verbose ("INITIALIZING writer %p to write basic value at value_pos %d of replacement string\n",
+ &writer, _dbus_string_get_length (&block.replacement));
+#endif
+ _dbus_type_writer_init_values_only (&writer,
+ reader->byte_order,
+ reader->type_str,
+ reader->type_pos,
+ &block.replacement,
+ _dbus_string_get_length (&block.replacement));
+#if RECURSIVE_MARSHAL_WRITE_TRACE
+ _dbus_verbose ("WRITING basic value to writer %p (replacement string)\n", &writer);
+#endif
+ if (!_dbus_type_writer_write_basic (&writer, current_type, value))
+ goto out;
+
+ if (!replacement_block_replace (&block,
+ reader,
+ realign_root))
+ goto out;
+
+ retval = TRUE;
+
+ out:
+ replacement_block_free (&block);
+ return retval;
+}
+
+static void
+reader_set_basic_fixed_length (DBusTypeReader *reader,
+ int current_type,
+ const void *value)
+{
+ _dbus_marshal_set_basic ((DBusString*) reader->value_str,
+ reader->value_pos,
+ current_type,
+ value,
+ reader->byte_order,
+ NULL, NULL);
+}
+
+/**
+ * Sets a new value for the basic type value pointed to by the reader,
+ * leaving the reader valid to continue reading. Any other readers
+ * will be invalidated if you set a variable-length type such as a
+ * string.
+ *
+ * The provided realign_root is the reader to start from when
+ * realigning the data that follows the newly-set value. The reader
+ * parameter must point to a value below the realign_root parameter.
+ * If the type being set is fixed-length, then realign_root may be
+ * #NULL. Only values reachable from realign_root will be realigned,
+ * so if your string contains other values you will need to deal with
+ * those somehow yourself. It is OK if realign_root is the same
+ * reader as the reader parameter, though if you aren't setting the
+ * root it may not be such a good idea.
+ *
+ * @todo DBusTypeReader currently takes "const" versions of the type
+ * and value strings, and this function modifies those strings by
+ * casting away the const, which is of course bad if we want to get
+ * picky. (To be truly clean you'd have an object which contained the
+ * type and value strings and set_basic would be a method on that
+ * object... this would also make DBusTypeReader the same thing as
+ * DBusTypeMark. But since DBusMessage is effectively that object for
+ * D-Bus it doesn't seem worth creating some random object.)
+ *
+ * @todo optimize this by only rewriting until the old and new values
+ * are at the same alignment. Frequently this should result in only
+ * replacing the value that's immediately at hand.
+ *
+ * @param reader reader indicating where to set a new value
+ * @param value address of the value to set
+ * @param realign_root realign from here
+ * @returns #FALSE if not enough memory
+ */
+dbus_bool_t
+_dbus_type_reader_set_basic (DBusTypeReader *reader,
+ const void *value,
+ const DBusTypeReader *realign_root)
+{
+ int current_type;
+
+ _dbus_assert (!reader->klass->types_only);
+ _dbus_assert (reader->value_str == realign_root->value_str);
+ _dbus_assert (reader->value_pos >= realign_root->value_pos);
+
+ current_type = _dbus_type_reader_get_current_type (reader);
+
+#if RECURSIVE_MARSHAL_WRITE_TRACE
+ _dbus_verbose (" SET BASIC type reader %p type_pos = %d value_pos = %d remaining sig '%s' realign_root = %p with value_pos %d current_type = %s\n",
+ reader, reader->type_pos, reader->value_pos,
+ _dbus_string_get_const_data_len (reader->type_str, reader->type_pos, 0),
+ realign_root,
+ realign_root ? realign_root->value_pos : -1,
+ _dbus_type_to_string (current_type));
+ _dbus_verbose_bytes_of_string (realign_root->value_str, realign_root->value_pos,
+ _dbus_string_get_length (realign_root->value_str) -
+ realign_root->value_pos);
+#endif
+
+ _dbus_assert (dbus_type_is_basic (current_type));
+
+ if (dbus_type_is_fixed (current_type))
+ {
+ reader_set_basic_fixed_length (reader, current_type, value);
+ return TRUE;
+ }
+ else
+ {
+ _dbus_assert (realign_root != NULL);
+ return reader_set_basic_variable_length (reader, current_type,
+ value, realign_root);
+ }
+}
+
+/**
+ * Recursively deletes any value pointed to by the reader, leaving the
+ * reader valid to continue reading. Any other readers will be
+ * invalidated.
+ *
+ * The provided realign_root is the reader to start from when
+ * realigning the data that follows the newly-set value.
+ * See _dbus_type_reader_set_basic() for more details on the
+ * realign_root paramter.
+ *
+ * @todo for now this does not delete the typecodes associated with
+ * the value, so this function should only be used for array elements.
+ *
+ * @param reader reader indicating where to delete a value
+ * @param realign_root realign from here
+ * @returns #FALSE if not enough memory
+ */
+dbus_bool_t
+_dbus_type_reader_delete (DBusTypeReader *reader,
+ const DBusTypeReader *realign_root)
+{
+ dbus_bool_t retval;
+ ReplacementBlock block;
+
+ _dbus_assert (realign_root != NULL);
+ _dbus_assert (reader->klass == &array_reader_class);
+
+ retval = FALSE;
+
+ if (!replacement_block_init (&block, reader))
+ return FALSE;
+
+ if (!replacement_block_replace (&block,
+ reader,
+ realign_root))
+ goto out;
+
+ retval = TRUE;
+
+ out:
+ replacement_block_free (&block);
+ return retval;
+}
+
+/*
+ * Compares two readers, which must be iterating over the same value data.
+ * Returns #TRUE if the first parameter is further along than the second parameter.
+ *
+ * @param lhs left-hand-side (first) parameter
+ * @param rhs left-hand-side (first) parameter
+ * @returns whether lhs is greater than rhs
+ */
+static dbus_bool_t
+_dbus_type_reader_greater_than (const DBusTypeReader *lhs,
+ const DBusTypeReader *rhs)
+{
+ _dbus_assert (lhs->value_str == rhs->value_str);
+
+ return lhs->value_pos > rhs->value_pos;
+}
+
+/*
+ *
+ *
+ * DBusTypeWriter
+ *
+ *
+ *
+ */
+
+/**
+ * Initialize a write iterator, which is used to write out values in
+ * serialized D-Bus format.
+ *
+ * The type_pos passed in is expected to be inside an already-valid,
+ * though potentially empty, type signature. This means that the byte
+ * after type_pos must be either #DBUS_TYPE_INVALID (aka nul) or some
+ * other valid type. #DBusTypeWriter won't enforce that the signature
+ * is already valid (you can append the nul byte at the end if you
+ * like), but just be aware that you need the nul byte eventually and
+ * #DBusTypeWriter isn't going to write it for you.
+ *
+ * @param writer the writer to init
+ * @param byte_order the byte order to marshal into
+ * @param type_str the string to write typecodes into
+ * @param type_pos where to insert typecodes
+ * @param value_str the string to write values into
+ * @param value_pos where to insert values
+ *
+ */
+void
+_dbus_type_writer_init (DBusTypeWriter *writer,
+ int byte_order,
+ DBusString *type_str,
+ int type_pos,
+ DBusString *value_str,
+ int value_pos)
+{
+ writer->byte_order = byte_order;
+ writer->type_str = type_str;
+ writer->type_pos = type_pos;
+ writer->value_str = value_str;
+ writer->value_pos = value_pos;
+ writer->container_type = DBUS_TYPE_INVALID;
+ writer->type_pos_is_expectation = FALSE;
+ writer->enabled = TRUE;
+
+#if RECURSIVE_MARSHAL_WRITE_TRACE
+ _dbus_verbose ("writer %p init remaining sig '%s'\n", writer,
+ writer->type_str ?
+ _dbus_string_get_const_data_len (writer->type_str, writer->type_pos, 0) :
+ "unknown");
+#endif
+}
+
+/**
+ * Initialize a write iterator, with the signature to be provided
+ * later.
+ *
+ * @param writer the writer to init
+ * @param byte_order the byte order to marshal into
+ * @param value_str the string to write values into
+ * @param value_pos where to insert values
+ *
+ */
+void
+_dbus_type_writer_init_types_delayed (DBusTypeWriter *writer,
+ int byte_order,
+ DBusString *value_str,
+ int value_pos)
+{
+ _dbus_type_writer_init (writer, byte_order,
+ NULL, 0, value_str, value_pos);
+}
+
+/**
+ * Adds type string to the writer, if it had none.
+ *
+ * @param writer the writer to init
+ * @param type_str type string to add
+ * @param type_pos type position
+ *
+ */
+void
+_dbus_type_writer_add_types (DBusTypeWriter *writer,
+ DBusString *type_str,
+ int type_pos)
+{
+ if (writer->type_str == NULL) /* keeps us from using this as setter */
+ {
+ writer->type_str = type_str;
+ writer->type_pos = type_pos;
+ }
+}
+
+/**
+ * Removes type string from the writer.
+ *
+ * @param writer the writer to remove from
+ */
+void
+_dbus_type_writer_remove_types (DBusTypeWriter *writer)
+{
+ writer->type_str = NULL;
+ writer->type_pos = -1;
+}
+
+/**
+ * Like _dbus_type_writer_init(), except the type string
+ * passed in should correspond to an existing signature that
+ * matches what you're going to write out. The writer will
+ * check what you write vs. this existing signature.
+ *
+ * @param writer the writer to init
+ * @param byte_order the byte order to marshal into
+ * @param type_str the string with signature
+ * @param type_pos start of signature
+ * @param value_str the string to write values into
+ * @param value_pos where to insert values
+ *
+ */
+void
+_dbus_type_writer_init_values_only (DBusTypeWriter *writer,
+ int byte_order,
+ const DBusString *type_str,
+ int type_pos,
+ DBusString *value_str,
+ int value_pos)
+{
+ _dbus_type_writer_init (writer, byte_order,
+ (DBusString*)type_str, type_pos,
+ value_str, value_pos);
+
+ writer->type_pos_is_expectation = TRUE;
+}
+
+static dbus_bool_t
+_dbus_type_writer_write_basic_no_typecode (DBusTypeWriter *writer,
+ int type,
+ const void *value)
+{
+ if (writer->enabled)
+ return _dbus_marshal_write_basic (writer->value_str,
+ writer->value_pos,
+ type,
+ value,
+ writer->byte_order,
+ &writer->value_pos);
+ else
+ return TRUE;
+}
+
+/* If our parent is an array, things are a little bit complicated.
+ *
+ * The parent must have a complete element type, such as
+ * "i" or "aai" or "(ii)" or "a(ii)". There can't be
+ * unclosed parens, or an "a" with no following type.
+ *
+ * To recurse, the only allowed operation is to recurse into the
+ * first type in the element type. So for "i" you can't recurse, for
+ * "ai" you can recurse into the array, for "(ii)" you can recurse
+ * into the struct.
+ *
+ * If you recurse into the array for "ai", then you must specify
+ * "i" for the element type of the array you recurse into.
+ *
+ * While inside an array at any level, we need to avoid writing to
+ * type_str, since the type only appears once for the whole array,
+ * it does not appear for each array element.
+ *
+ * While inside an array type_pos points to the expected next
+ * typecode, rather than the next place we could write a typecode.
+ */
+static void
+writer_recurse_init_and_check (DBusTypeWriter *writer,
+ int container_type,
+ DBusTypeWriter *sub)
+{
+ _dbus_type_writer_init (sub,
+ writer->byte_order,
+ writer->type_str,
+ writer->type_pos,
+ writer->value_str,
+ writer->value_pos);
+
+ sub->container_type = container_type;
+
+ if (writer->type_pos_is_expectation ||
+ (sub->container_type == DBUS_TYPE_ARRAY || sub->container_type == DBUS_TYPE_VARIANT))
+ sub->type_pos_is_expectation = TRUE;
+ else
+ sub->type_pos_is_expectation = FALSE;
+
+ sub->enabled = writer->enabled;
+
+#ifndef DBUS_DISABLE_CHECKS
+ if (writer->type_pos_is_expectation && writer->type_str)
+ {
+ int expected;
+
+ expected = _dbus_first_type_in_signature (writer->type_str, writer->type_pos);
+
+ if (expected != sub->container_type)
+ {
+ if (expected != DBUS_TYPE_INVALID)
+ _dbus_warn_check_failed ("Writing an element of type %s, but the expected type here is %s\n"
+ "The overall signature expected here was '%s' and we are on byte %d of that signature.",
+ _dbus_type_to_string (sub->container_type),
+ _dbus_type_to_string (expected),
+ _dbus_string_get_const_data (writer->type_str), writer->type_pos);
+ else
+ _dbus_warn_check_failed ("Writing an element of type %s, but no value is expected here\n"
+ "The overall signature expected here was '%s' and we are on byte %d of that signature.",
+ _dbus_type_to_string (sub->container_type),
+ _dbus_string_get_const_data (writer->type_str), writer->type_pos);
+
+ _dbus_assert_not_reached ("bad array element or variant content written");
+ }
+ }
+#endif /* DBUS_DISABLE_CHECKS */
+
+#if RECURSIVE_MARSHAL_WRITE_TRACE
+ _dbus_verbose (" type writer %p recurse parent %s type_pos = %d value_pos = %d is_expectation = %d remaining sig '%s' enabled = %d\n",
+ writer,
+ _dbus_type_to_string (writer->container_type),
+ writer->type_pos, writer->value_pos, writer->type_pos_is_expectation,
+ writer->type_str ?
+ _dbus_string_get_const_data_len (writer->type_str, writer->type_pos, 0) :
+ "unknown",
+ writer->enabled);
+ _dbus_verbose (" type writer %p recurse sub %s type_pos = %d value_pos = %d is_expectation = %d enabled = %d\n",
+ sub,
+ _dbus_type_to_string (sub->container_type),
+ sub->type_pos, sub->value_pos,
+ sub->type_pos_is_expectation,
+ sub->enabled);
+#endif
+}
+
+static dbus_bool_t
+write_or_verify_typecode (DBusTypeWriter *writer,
+ int typecode)
+{
+ /* A subwriter inside an array or variant will have type_pos
+ * pointing to the expected typecode; a writer not inside an array
+ * or variant has type_pos pointing to the next place to insert a
+ * typecode.
+ */
+#if RECURSIVE_MARSHAL_WRITE_TRACE
+ _dbus_verbose (" type writer %p write_or_verify start type_pos = %d remaining sig '%s' enabled = %d\n",
+ writer, writer->type_pos,
+ writer->type_str ?
+ _dbus_string_get_const_data_len (writer->type_str, writer->type_pos, 0) :
+ "unknown",
+ writer->enabled);
+#endif
+
+ if (writer->type_str == NULL)
+ return TRUE;
+
+ if (writer->type_pos_is_expectation)
+ {
+#ifndef DBUS_DISABLE_CHECKS
+ {
+ int expected;
+
+ expected = _dbus_string_get_byte (writer->type_str, writer->type_pos);
+
+ if (expected != typecode)
+ {
+ if (expected != DBUS_TYPE_INVALID)
+ _dbus_warn_check_failed ("Array or variant type requires that type %s be written, but %s was written.\n"
+ "The overall signature expected here was '%s' and we are on byte %d of that signature.",
+ _dbus_type_to_string (expected), _dbus_type_to_string (typecode),
+ _dbus_string_get_const_data (writer->type_str), writer->type_pos);
+ else
+ _dbus_warn_check_failed ("Array or variant type wasn't expecting any more values to be written into it, but a value %s was written.\n"
+ "The overall signature expected here was '%s' and we are on byte %d of that signature.",
+ _dbus_type_to_string (typecode),
+ _dbus_string_get_const_data (writer->type_str), writer->type_pos);
+ _dbus_assert_not_reached ("bad type inserted somewhere inside an array or variant");
+ }
+ }
+#endif /* DBUS_DISABLE_CHECKS */
+
+ /* if immediately inside an array we'd always be appending an element,
+ * so the expected type doesn't change; if inside a struct or something
+ * below an array, we need to move through said struct or something.
+ */
+ if (writer->container_type != DBUS_TYPE_ARRAY)
+ writer->type_pos += 1;
+ }
+ else
+ {
+ if (!_dbus_string_insert_byte (writer->type_str,
+ writer->type_pos,
+ typecode))
+ return FALSE;
+
+ writer->type_pos += 1;
+ }
+
+#if RECURSIVE_MARSHAL_WRITE_TRACE
+ _dbus_verbose (" type writer %p write_or_verify end type_pos = %d remaining sig '%s'\n",
+ writer, writer->type_pos,
+ _dbus_string_get_const_data_len (writer->type_str, writer->type_pos, 0));
+#endif
+
+ return TRUE;
+}
+
+static dbus_bool_t
+writer_recurse_struct_or_dict_entry (DBusTypeWriter *writer,
+ int begin_char,
+ const DBusString *contained_type,
+ int contained_type_start,
+ int contained_type_len,
+ DBusTypeWriter *sub)
+{
+ /* FIXME right now contained_type is ignored; we could probably
+ * almost trivially fix the code so if it's present we
+ * write it out and then set type_pos_is_expectation
+ */
+
+ /* Ensure that we'll be able to add alignment padding and the typecode */
+ if (writer->enabled)
+ {
+ if (!_dbus_string_alloc_space (sub->value_str, 8))
+ return FALSE;
+ }
+
+ if (!write_or_verify_typecode (sub, begin_char))
+ _dbus_assert_not_reached ("failed to insert struct typecode after prealloc");
+
+ if (writer->enabled)
+ {
+ if (!_dbus_string_insert_bytes (sub->value_str,
+ sub->value_pos,
+ _DBUS_ALIGN_VALUE (sub->value_pos, 8) - sub->value_pos,
+ '\0'))
+ _dbus_assert_not_reached ("should not have failed to insert alignment padding for struct");
+ sub->value_pos = _DBUS_ALIGN_VALUE (sub->value_pos, 8);
+ }
+
+ return TRUE;
+}
+
+
+static dbus_bool_t
+writer_recurse_array (DBusTypeWriter *writer,
+ const DBusString *contained_type,
+ int contained_type_start,
+ int contained_type_len,
+ DBusTypeWriter *sub,
+ dbus_bool_t is_array_append)
+{
+ dbus_uint32_t value = 0;
+ int alignment;
+ int aligned;
+
+#ifndef DBUS_DISABLE_CHECKS
+ if (writer->container_type == DBUS_TYPE_ARRAY &&
+ writer->type_str)
+ {
+ if (!_dbus_string_equal_substring (contained_type,
+ contained_type_start,
+ contained_type_len,
+ writer->type_str,
+ writer->u.array.element_type_pos + 1))
+ {
+ _dbus_warn_check_failed ("Writing an array of '%s' but this is incompatible with the expected type of elements in the parent array",
+ _dbus_string_get_const_data_len (contained_type,
+ contained_type_start,
+ contained_type_len));
+ _dbus_assert_not_reached ("incompatible type for child array");
+ }
+ }
+#endif /* DBUS_DISABLE_CHECKS */
+
+ if (writer->enabled && !is_array_append)
+ {
+ /* 3 pad + 4 bytes for the array length, and 4 bytes possible padding
+ * before array values
+ */
+ if (!_dbus_string_alloc_space (sub->value_str, 3 + 4 + 4))
+ return FALSE;
+ }
+
+ if (writer->type_str != NULL)
+ {
+ sub->type_pos += 1; /* move to point to the element type, since type_pos
+ * should be the expected type for further writes
+ */
+ sub->u.array.element_type_pos = sub->type_pos;
+ }
+
+ if (!writer->type_pos_is_expectation)
+ {
+ /* sub is a toplevel/outermost array so we need to write the type data */
+
+ /* alloc space for array typecode, element signature */
+ if (!_dbus_string_alloc_space (writer->type_str, 1 + contained_type_len))
+ return FALSE;
+
+ if (!_dbus_string_insert_byte (writer->type_str,
+ writer->type_pos,
+ DBUS_TYPE_ARRAY))
+ _dbus_assert_not_reached ("failed to insert array typecode after prealloc");
+
+ if (!_dbus_string_copy_len (contained_type,
+ contained_type_start, contained_type_len,
+ sub->type_str,
+ sub->u.array.element_type_pos))
+ _dbus_assert_not_reached ("should not have failed to insert array element typecodes");
+ }
+
+ if (writer->type_str != NULL)
+ {
+ /* If the parent is an array, we hold type_pos pointing at the array element type;
+ * otherwise advance it to reflect the array value we just recursed into
+ */
+ if (writer->container_type != DBUS_TYPE_ARRAY)
+ writer->type_pos += 1 + contained_type_len;
+ else
+ _dbus_assert (writer->type_pos_is_expectation); /* because it's an array */
+ }
+
+ if (writer->enabled)
+ {
+ /* Write (or jump over, if is_array_append) the length */
+ sub->u.array.len_pos = _DBUS_ALIGN_VALUE (sub->value_pos, 4);
+
+ if (is_array_append)
+ {
+ sub->value_pos += 4;
+ }
+ else
+ {
+ if (!_dbus_type_writer_write_basic_no_typecode (sub, DBUS_TYPE_UINT32,
+ &value))
+ _dbus_assert_not_reached ("should not have failed to insert array len");
+ }
+
+ _dbus_assert (sub->u.array.len_pos == sub->value_pos - 4);
+
+ /* Write alignment padding for array elements
+ * Note that we write the padding *even for empty arrays*
+ * to avoid wonky special cases
+ */
+ alignment = element_type_get_alignment (contained_type, contained_type_start);
+
+ aligned = _DBUS_ALIGN_VALUE (sub->value_pos, alignment);
+ if (aligned != sub->value_pos)
+ {
+ if (!is_array_append)
+ {
+ if (!_dbus_string_insert_bytes (sub->value_str,
+ sub->value_pos,
+ aligned - sub->value_pos,
+ '\0'))
+ _dbus_assert_not_reached ("should not have failed to insert alignment padding");
+ }
+
+ sub->value_pos = aligned;
+ }
+
+ sub->u.array.start_pos = sub->value_pos;
+
+ if (is_array_append)
+ {
+ dbus_uint32_t len;
+
+ _dbus_assert (_DBUS_ALIGN_VALUE (sub->u.array.len_pos, 4) ==
+ (unsigned) sub->u.array.len_pos);
+ len = _dbus_unpack_uint32 (sub->byte_order,
+ _dbus_string_get_const_udata_len (sub->value_str,
+ sub->u.array.len_pos,
+ 4));
+
+ sub->value_pos += len;
+ }
+ }
+ else
+ {
+ /* not enabled, so we won't write the len_pos; set it to -1 to so indicate */
+ sub->u.array.len_pos = -1;
+ sub->u.array.start_pos = sub->value_pos;
+ }
+
+ _dbus_assert (sub->u.array.len_pos < sub->u.array.start_pos);
+ _dbus_assert (is_array_append || sub->u.array.start_pos == sub->value_pos);
+
+#if RECURSIVE_MARSHAL_WRITE_TRACE
+ _dbus_verbose (" type writer %p recurse array done remaining sig '%s' array start_pos = %d len_pos = %d value_pos = %d\n", sub,
+ sub->type_str ?
+ _dbus_string_get_const_data_len (sub->type_str, sub->type_pos, 0) :
+ "unknown",
+ sub->u.array.start_pos, sub->u.array.len_pos, sub->value_pos);
+#endif
+
+ return TRUE;
+}
+
+/* Variant value will normally have:
+ * 1 byte signature length not including nul
+ * signature typecodes (nul terminated)
+ * padding to alignment of contained type
+ * body according to signature
+ *
+ * The signature string can only have a single type
+ * in it but that type may be complex/recursive.
+ *
+ * So a typical variant type with the integer 3 will have these
+ * octets:
+ * 0x1 'i' '\0' [1 byte padding to alignment boundary] 0x0 0x0 0x0 0x3
+ *
+ * The main world of hurt for writing out a variant is that the type
+ * string is the same string as the value string. Which means
+ * inserting to the type string will move the value_pos; and it means
+ * that inserting to the type string could break type alignment.
+ */
+static dbus_bool_t
+writer_recurse_variant (DBusTypeWriter *writer,
+ const DBusString *contained_type,
+ int contained_type_start,
+ int contained_type_len,
+ DBusTypeWriter *sub)
+{
+ int contained_alignment;
+
+ if (writer->enabled)
+ {
+ /* Allocate space for the worst case, which is 1 byte sig
+ * length, nul byte at end of sig, and 7 bytes padding to
+ * 8-boundary.
+ */
+ if (!_dbus_string_alloc_space (sub->value_str, contained_type_len + 9))
+ return FALSE;
+ }
+
+ /* write VARIANT typecode to the parent's type string */
+ if (!write_or_verify_typecode (writer, DBUS_TYPE_VARIANT))
+ return FALSE;
+
+ /* If not enabled, mark that we have no type_str anymore ... */
+
+ if (!writer->enabled)
+ {
+ sub->type_str = NULL;
+ sub->type_pos = -1;
+
+ return TRUE;
+ }
+
+ /* If we're enabled then continue ... */
+
+ if (!_dbus_string_insert_byte (sub->value_str,
+ sub->value_pos,
+ contained_type_len))
+ _dbus_assert_not_reached ("should not have failed to insert variant type sig len");
+
+ sub->value_pos += 1;
+
+ /* Here we switch over to the expected type sig we're about to write */
+ sub->type_str = sub->value_str;
+ sub->type_pos = sub->value_pos;
+
+ if (!_dbus_string_copy_len (contained_type, contained_type_start, contained_type_len,
+ sub->value_str, sub->value_pos))
+ _dbus_assert_not_reached ("should not have failed to insert variant type sig");
+
+ sub->value_pos += contained_type_len;
+
+ if (!_dbus_string_insert_byte (sub->value_str,
+ sub->value_pos,
+ DBUS_TYPE_INVALID))
+ _dbus_assert_not_reached ("should not have failed to insert variant type nul termination");
+
+ sub->value_pos += 1;
+
+ contained_alignment = _dbus_type_get_alignment (_dbus_first_type_in_signature (contained_type, contained_type_start));
+
+ if (!_dbus_string_insert_bytes (sub->value_str,
+ sub->value_pos,
+ _DBUS_ALIGN_VALUE (sub->value_pos, contained_alignment) - sub->value_pos,
+ '\0'))
+ _dbus_assert_not_reached ("should not have failed to insert alignment padding for variant body");
+ sub->value_pos = _DBUS_ALIGN_VALUE (sub->value_pos, contained_alignment);
+
+ return TRUE;
+}
+
+static dbus_bool_t
+_dbus_type_writer_recurse_contained_len (DBusTypeWriter *writer,
+ int container_type,
+ const DBusString *contained_type,
+ int contained_type_start,
+ int contained_type_len,
+ DBusTypeWriter *sub,
+ dbus_bool_t is_array_append)
+{
+ writer_recurse_init_and_check (writer, container_type, sub);
+
+ switch (container_type)
+ {
+ case DBUS_TYPE_STRUCT:
+ return writer_recurse_struct_or_dict_entry (writer,
+ DBUS_STRUCT_BEGIN_CHAR,
+ contained_type,
+ contained_type_start, contained_type_len,
+ sub);
+ break;
+ case DBUS_TYPE_DICT_ENTRY:
+ return writer_recurse_struct_or_dict_entry (writer,
+ DBUS_DICT_ENTRY_BEGIN_CHAR,
+ contained_type,
+ contained_type_start, contained_type_len,
+ sub);
+ break;
+ case DBUS_TYPE_ARRAY:
+ return writer_recurse_array (writer,
+ contained_type, contained_type_start, contained_type_len,
+ sub, is_array_append);
+ break;
+ case DBUS_TYPE_VARIANT:
+ return writer_recurse_variant (writer,
+ contained_type, contained_type_start, contained_type_len,
+ sub);
+ break;
+ default:
+ _dbus_assert_not_reached ("tried to recurse into type that doesn't support that");
+ return FALSE;
+ break;
+ }
+}
+
+/**
+ * Opens a new container and writes out the initial information for that container.
+ *
+ * @param writer the writer
+ * @param container_type the type of the container to open
+ * @param contained_type the array element type or variant content type
+ * @param contained_type_start position to look for the type
+ * @param sub the new sub-writer to write container contents
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_type_writer_recurse (DBusTypeWriter *writer,
+ int container_type,
+ const DBusString *contained_type,
+ int contained_type_start,
+ DBusTypeWriter *sub)
+{
+ int contained_type_len;
+
+ if (contained_type)
+ contained_type_len = find_len_of_complete_type (contained_type, contained_type_start);
+ else
+ contained_type_len = 0;
+
+ return _dbus_type_writer_recurse_contained_len (writer, container_type,
+ contained_type,
+ contained_type_start,
+ contained_type_len,
+ sub,
+ FALSE);
+}
+
+/**
+ * Append to an existing array. Essentially, the writer will read an
+ * existing length at the write location; jump over that length; and
+ * write new fields. On unrecurse(), the existing length will be
+ * updated.
+ *
+ * @param writer the writer
+ * @param contained_type element type
+ * @param contained_type_start position of element type
+ * @param sub the subwriter to init
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_type_writer_append_array (DBusTypeWriter *writer,
+ const DBusString *contained_type,
+ int contained_type_start,
+ DBusTypeWriter *sub)
+{
+ int contained_type_len;
+
+ if (contained_type)
+ contained_type_len = find_len_of_complete_type (contained_type, contained_type_start);
+ else
+ contained_type_len = 0;
+
+ return _dbus_type_writer_recurse_contained_len (writer, DBUS_TYPE_ARRAY,
+ contained_type,
+ contained_type_start,
+ contained_type_len,
+ sub,
+ TRUE);
+}
+
+static int
+writer_get_array_len (DBusTypeWriter *writer)
+{
+ _dbus_assert (writer->container_type == DBUS_TYPE_ARRAY);
+ return writer->value_pos - writer->u.array.start_pos;
+}
+
+/**
+ * Closes a container created by _dbus_type_writer_recurse()
+ * and writes any additional information to the values block.
+ *
+ * @param writer the writer
+ * @param sub the sub-writer created by _dbus_type_writer_recurse()
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_type_writer_unrecurse (DBusTypeWriter *writer,
+ DBusTypeWriter *sub)
+{
+ /* type_pos_is_expectation never gets unset once set, or we'd get all hosed */
+ _dbus_assert (!writer->type_pos_is_expectation ||
+ (writer->type_pos_is_expectation && sub->type_pos_is_expectation));
+
+#if RECURSIVE_MARSHAL_WRITE_TRACE
+ _dbus_verbose (" type writer %p unrecurse type_pos = %d value_pos = %d is_expectation = %d container_type = %s\n",
+ writer, writer->type_pos, writer->value_pos, writer->type_pos_is_expectation,
+ _dbus_type_to_string (writer->container_type));
+ _dbus_verbose (" type writer %p unrecurse sub type_pos = %d value_pos = %d is_expectation = %d container_type = %s\n",
+ sub, sub->type_pos, sub->value_pos,
+ sub->type_pos_is_expectation,
+ _dbus_type_to_string (sub->container_type));
+#endif
+
+ if (sub->container_type == DBUS_TYPE_STRUCT)
+ {
+ if (!write_or_verify_typecode (sub, DBUS_STRUCT_END_CHAR))
+ return FALSE;
+ }
+ else if (sub->container_type == DBUS_TYPE_DICT_ENTRY)
+ {
+ if (!write_or_verify_typecode (sub, DBUS_DICT_ENTRY_END_CHAR))
+ return FALSE;
+ }
+ else if (sub->container_type == DBUS_TYPE_ARRAY)
+ {
+ if (sub->u.array.len_pos >= 0) /* len_pos == -1 if we weren't enabled when we passed it */
+ {
+ dbus_uint32_t len;
+
+ /* Set the array length */
+ len = writer_get_array_len (sub);
+ _dbus_marshal_set_uint32 (sub->value_str,
+ sub->u.array.len_pos,
+ len,
+ sub->byte_order);
+#if RECURSIVE_MARSHAL_WRITE_TRACE
+ _dbus_verbose (" filled in sub array len to %u at len_pos %d\n",
+ len, sub->u.array.len_pos);
+#endif
+ }
+#if RECURSIVE_MARSHAL_WRITE_TRACE
+ else
+ {
+ _dbus_verbose (" not filling in sub array len because we were disabled when we passed the len\n");
+ }
+#endif
+ }
+
+ /* Now get type_pos right for the parent writer. Here are the cases:
+ *
+ * Cases !writer->type_pos_is_expectation:
+ * (in these cases we want to update to the new insertion point)
+ *
+ * - if we recursed into a STRUCT then we didn't know in advance
+ * what the types in the struct would be; so we have to fill in
+ * that information now.
+ * writer->type_pos = sub->type_pos
+ *
+ * - if we recursed into anything else, we knew the full array
+ * type, or knew the single typecode marking VARIANT, so
+ * writer->type_pos is already correct.
+ * writer->type_pos should remain as-is
+ *
+ * - note that the parent is never an ARRAY or VARIANT, if it were
+ * then type_pos_is_expectation would be TRUE. The parent
+ * is thus known to be a toplevel or STRUCT.
+ *
+ * Cases where writer->type_pos_is_expectation:
+ * (in these cases we want to update to next expected type to write)
+ *
+ * - we recursed from STRUCT into STRUCT and we didn't increment
+ * type_pos in the parent just to stay consistent with the
+ * !writer->type_pos_is_expectation case (though we could
+ * special-case this in recurse_struct instead if we wanted)
+ * writer->type_pos = sub->type_pos
+ *
+ * - we recursed from STRUCT into ARRAY or VARIANT and type_pos
+ * for parent should have been incremented already
+ * writer->type_pos should remain as-is
+ *
+ * - we recursed from ARRAY into a sub-element, so type_pos in the
+ * parent is the element type and should remain the element type
+ * for the benefit of the next child element
+ * writer->type_pos should remain as-is
+ *
+ * - we recursed from VARIANT into its value, so type_pos in the
+ * parent makes no difference since there's only one value
+ * and we just finished writing it and won't use type_pos again
+ * writer->type_pos should remain as-is
+ *
+ *
+ * For all these, DICT_ENTRY is the same as STRUCT
+ */
+ if (writer->type_str != NULL)
+ {
+ if ((sub->container_type == DBUS_TYPE_STRUCT ||
+ sub->container_type == DBUS_TYPE_DICT_ENTRY) &&
+ (writer->container_type == DBUS_TYPE_STRUCT ||
+ writer->container_type == DBUS_TYPE_DICT_ENTRY ||
+ writer->container_type == DBUS_TYPE_INVALID))
+ {
+ /* Advance the parent to the next struct field */
+ writer->type_pos = sub->type_pos;
+ }
+ }
+
+ writer->value_pos = sub->value_pos;
+
+#if RECURSIVE_MARSHAL_WRITE_TRACE
+ _dbus_verbose (" type writer %p unrecursed type_pos = %d value_pos = %d remaining sig '%s'\n",
+ writer, writer->type_pos, writer->value_pos,
+ writer->type_str ?
+ _dbus_string_get_const_data_len (writer->type_str, writer->type_pos, 0) :
+ "unknown");
+#endif
+
+ return TRUE;
+}
+
+/**
+ * Writes out a basic type.
+ *
+ * @param writer the writer
+ * @param type the type to write
+ * @param value the address of the value to write
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_type_writer_write_basic (DBusTypeWriter *writer,
+ int type,
+ const void *value)
+{
+ dbus_bool_t retval;
+
+ /* First ensure that our type realloc will succeed */
+ if (!writer->type_pos_is_expectation && writer->type_str != NULL)
+ {
+ if (!_dbus_string_alloc_space (writer->type_str, 1))
+ return FALSE;
+ }
+
+ retval = FALSE;
+
+ if (!_dbus_type_writer_write_basic_no_typecode (writer, type, value))
+ goto out;
+
+ if (!write_or_verify_typecode (writer, type))
+ _dbus_assert_not_reached ("failed to write typecode after prealloc");
+
+ retval = TRUE;
+
+ out:
+#if RECURSIVE_MARSHAL_WRITE_TRACE
+ _dbus_verbose (" type writer %p basic type_pos = %d value_pos = %d is_expectation = %d enabled = %d\n",
+ writer, writer->type_pos, writer->value_pos, writer->type_pos_is_expectation,
+ writer->enabled);
+#endif
+
+ return retval;
+}
+
+/**
+ * Writes a block of fixed-length basic values, i.e. those that are
+ * both dbus_type_is_fixed() and _dbus_type_is_basic(). The block
+ * must be written inside an array.
+ *
+ * The value parameter should be the address of said array of values,
+ * so e.g. if it's an array of double, pass in "const double**"
+ *
+ * @param writer the writer
+ * @param element_type type of stuff in the array
+ * @param value address of the array
+ * @param n_elements number of elements in the array
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_type_writer_write_fixed_multi (DBusTypeWriter *writer,
+ int element_type,
+ const void *value,
+ int n_elements)
+{
+ _dbus_assert (writer->container_type == DBUS_TYPE_ARRAY);
+ _dbus_assert (dbus_type_is_fixed (element_type));
+ _dbus_assert (writer->type_pos_is_expectation);
+ _dbus_assert (n_elements >= 0);
+
+#if RECURSIVE_MARSHAL_WRITE_TRACE
+ _dbus_verbose (" type writer %p entering fixed multi type_pos = %d value_pos = %d n_elements %d\n",
+ writer, writer->type_pos, writer->value_pos, n_elements);
+#endif
+
+ if (!write_or_verify_typecode (writer, element_type))
+ _dbus_assert_not_reached ("OOM should not happen if only verifying typecode");
+
+ if (writer->enabled)
+ {
+ if (!_dbus_marshal_write_fixed_multi (writer->value_str,
+ writer->value_pos,
+ element_type,
+ value,
+ n_elements,
+ writer->byte_order,
+ &writer->value_pos))
+ return FALSE;
+ }
+
+#if RECURSIVE_MARSHAL_WRITE_TRACE
+ _dbus_verbose (" type writer %p fixed multi written new type_pos = %d new value_pos = %d n_elements %d\n",
+ writer, writer->type_pos, writer->value_pos, n_elements);
+#endif
+
+ return TRUE;
+}
+
+static void
+enable_if_after (DBusTypeWriter *writer,
+ DBusTypeReader *reader,
+ const DBusTypeReader *start_after)
+{
+ if (start_after)
+ {
+ if (!writer->enabled && _dbus_type_reader_greater_than (reader, start_after))
+ {
+ _dbus_type_writer_set_enabled (writer, TRUE);
+#if RECURSIVE_MARSHAL_WRITE_TRACE
+ _dbus_verbose ("ENABLING writer %p at %d because reader at value_pos %d is after reader at value_pos %d\n",
+ writer, writer->value_pos, reader->value_pos, start_after->value_pos);
+#endif
+ }
+
+ _dbus_assert ((!writer->enabled && !_dbus_type_reader_greater_than (reader, start_after)) ||
+ (writer->enabled && _dbus_type_reader_greater_than (reader, start_after)));
+ }
+}
+
+static dbus_bool_t
+append_fixup (DBusList **fixups,
+ const DBusArrayLenFixup *fixup)
+{
+ DBusArrayLenFixup *f;
+
+ f = dbus_new (DBusArrayLenFixup, 1);
+ if (f == NULL)
+ return FALSE;
+
+ *f = *fixup;
+
+ if (!_dbus_list_append (fixups, f))
+ {
+ dbus_free (f);
+ return FALSE;
+ }
+
+ _dbus_assert (f->len_pos_in_reader == fixup->len_pos_in_reader);
+ _dbus_assert (f->new_len == fixup->new_len);
+
+ return TRUE;
+}
+
+/* This loop is trivial if you ignore all the start_after nonsense,
+ * so if you're trying to figure it out, start by ignoring that
+ */
+static dbus_bool_t
+writer_write_reader_helper (DBusTypeWriter *writer,
+ DBusTypeReader *reader,
+ const DBusTypeReader *start_after,
+ int start_after_new_pos,
+ int start_after_new_len,
+ DBusList **fixups,
+ dbus_bool_t inside_start_after)
+{
+ int current_type;
+
+ while ((current_type = _dbus_type_reader_get_current_type (reader)) != DBUS_TYPE_INVALID)
+ {
+ if (dbus_type_is_container (current_type))
+ {
+ DBusTypeReader subreader;
+ DBusTypeWriter subwriter;
+ const DBusString *sig_str;
+ int sig_start;
+ int sig_len;
+ dbus_bool_t enabled_at_recurse;
+ dbus_bool_t past_start_after;
+ int reader_array_len_pos;
+ int reader_array_start_pos;
+ dbus_bool_t this_is_start_after;
+
+ /* type_pos is checked since e.g. in a struct the struct
+ * and its first field have the same value_pos.
+ * type_str will differ in reader/start_after for variants
+ * where type_str is inside the value_str
+ */
+ if (!inside_start_after && start_after &&
+ reader->value_pos == start_after->value_pos &&
+ reader->type_str == start_after->type_str &&
+ reader->type_pos == start_after->type_pos)
+ this_is_start_after = TRUE;
+ else
+ this_is_start_after = FALSE;
+
+ _dbus_type_reader_recurse (reader, &subreader);
+
+ if (current_type == DBUS_TYPE_ARRAY)
+ {
+ reader_array_len_pos = ARRAY_READER_LEN_POS (&subreader);
+ reader_array_start_pos = subreader.u.array.start_pos;
+ }
+ else
+ {
+ /* quiet gcc */
+ reader_array_len_pos = -1;
+ reader_array_start_pos = -1;
+ }
+
+ _dbus_type_reader_get_signature (&subreader, &sig_str,
+ &sig_start, &sig_len);
+
+#if RECURSIVE_MARSHAL_WRITE_TRACE
+ _dbus_verbose ("about to recurse into %s reader at %d subreader at %d writer at %d start_after reader at %d write target len %d inside_start_after = %d this_is_start_after = %d\n",
+ _dbus_type_to_string (current_type),
+ reader->value_pos,
+ subreader.value_pos,
+ writer->value_pos,
+ start_after ? start_after->value_pos : -1,
+ _dbus_string_get_length (writer->value_str),
+ inside_start_after, this_is_start_after);
+#endif
+
+ if (!inside_start_after && !this_is_start_after)
+ enable_if_after (writer, &subreader, start_after);
+ enabled_at_recurse = writer->enabled;
+ if (!_dbus_type_writer_recurse_contained_len (writer, current_type,
+ sig_str, sig_start, sig_len,
+ &subwriter, FALSE))
+ goto oom;
+
+#if RECURSIVE_MARSHAL_WRITE_TRACE
+ _dbus_verbose ("recursed into subwriter at %d write target len %d\n",
+ subwriter.value_pos,
+ _dbus_string_get_length (subwriter.value_str));
+#endif
+
+ if (!writer_write_reader_helper (&subwriter, &subreader, start_after,
+ start_after_new_pos, start_after_new_len,
+ fixups,
+ inside_start_after ||
+ this_is_start_after))
+ goto oom;
+
+#if RECURSIVE_MARSHAL_WRITE_TRACE
+ _dbus_verbose ("about to unrecurse from %s subreader at %d writer at %d subwriter at %d write target len %d\n",
+ _dbus_type_to_string (current_type),
+ subreader.value_pos,
+ writer->value_pos,
+ subwriter.value_pos,
+ _dbus_string_get_length (writer->value_str));
+#endif
+
+ if (!inside_start_after && !this_is_start_after)
+ enable_if_after (writer, &subreader, start_after);
+ past_start_after = writer->enabled;
+ if (!_dbus_type_writer_unrecurse (writer, &subwriter))
+ goto oom;
+
+ /* If we weren't enabled when we recursed, we didn't
+ * write an array len; if we passed start_after
+ * somewhere inside the array, then we need to generate
+ * a fixup.
+ */
+ if (start_after != NULL &&
+ !enabled_at_recurse && past_start_after &&
+ current_type == DBUS_TYPE_ARRAY &&
+ fixups != NULL)
+ {
+ DBusArrayLenFixup fixup;
+ int bytes_written_after_start_after;
+ int bytes_before_start_after;
+ int old_len;
+
+ /* this subwriter access is moderately unkosher since we
+ * already unrecursed, but it works as long as unrecurse
+ * doesn't break us on purpose
+ */
+ bytes_written_after_start_after = writer_get_array_len (&subwriter);
+
+ bytes_before_start_after =
+ start_after->value_pos - reader_array_start_pos;
+
+ fixup.len_pos_in_reader = reader_array_len_pos;
+ fixup.new_len =
+ bytes_before_start_after +
+ start_after_new_len +
+ bytes_written_after_start_after;
+
+ _dbus_assert (_DBUS_ALIGN_VALUE (fixup.len_pos_in_reader, 4) ==
+ (unsigned) fixup.len_pos_in_reader);
+
+ old_len = _dbus_unpack_uint32 (reader->byte_order,
+ _dbus_string_get_const_udata_len (reader->value_str,
+ fixup.len_pos_in_reader, 4));
+
+ if (old_len != fixup.new_len && !append_fixup (fixups, &fixup))
+ goto oom;
+
+#if RECURSIVE_MARSHAL_WRITE_TRACE
+ _dbus_verbose ("Generated fixup len_pos_in_reader = %d new_len = %d reader_array_start_pos = %d start_after->value_pos = %d bytes_before_start_after = %d start_after_new_len = %d bytes_written_after_start_after = %d\n",
+ fixup.len_pos_in_reader,
+ fixup.new_len,
+ reader_array_start_pos,
+ start_after->value_pos,
+ bytes_before_start_after,
+ start_after_new_len,
+ bytes_written_after_start_after);
+#endif
+ }
+ }
+ else
+ {
+ DBusBasicValue val;
+
+ _dbus_assert (dbus_type_is_basic (current_type));
+
+#if RECURSIVE_MARSHAL_WRITE_TRACE
+ _dbus_verbose ("Reading basic value %s at %d\n",
+ _dbus_type_to_string (current_type),
+ reader->value_pos);
+#endif
+
+ _dbus_type_reader_read_basic (reader, &val);
+
+#if RECURSIVE_MARSHAL_WRITE_TRACE
+ _dbus_verbose ("Writing basic value %s at %d write target len %d inside_start_after = %d\n",
+ _dbus_type_to_string (current_type),
+ writer->value_pos,
+ _dbus_string_get_length (writer->value_str),
+ inside_start_after);
+#endif
+ if (!inside_start_after)
+ enable_if_after (writer, reader, start_after);
+ if (!_dbus_type_writer_write_basic (writer, current_type, &val))
+ goto oom;
+#if RECURSIVE_MARSHAL_WRITE_TRACE
+ _dbus_verbose ("Wrote basic value %s, new value_pos %d write target len %d\n",
+ _dbus_type_to_string (current_type),
+ writer->value_pos,
+ _dbus_string_get_length (writer->value_str));
+#endif
+ }
+
+ _dbus_type_reader_next (reader);
+ }
+
+ return TRUE;
+
+ oom:
+ if (fixups)
+ apply_and_free_fixups (fixups, NULL); /* NULL for reader to apply to */
+
+ return FALSE;
+}
+
+/*
+ * Iterate through all values in the given reader, writing a copy of
+ * each value to the writer. The reader will be moved forward to its
+ * end position.
+ *
+ * If a reader start_after is provided, it should be a reader for the
+ * same data as the reader to be written. Only values occurring after
+ * the value pointed to by start_after will be written to the writer.
+ *
+ * If start_after is provided, then the copy of the reader will be
+ * partial. This means that array lengths will not have been copied.
+ * The assumption is that you wrote a new version of the value at
+ * start_after to the writer. You have to pass in the start position
+ * and length of the new value. (If you are deleting the value
+ * at start_after, pass in 0 for the length.)
+ *
+ * If the fixups parameter is non-#NULL, then any array length that
+ * was read but not written due to start_after will be provided
+ * as a #DBusArrayLenFixup. The fixup contains the position of the
+ * array length in the source data, and the correct array length
+ * assuming you combine the source data before start_after with
+ * the written data at start_after and beyond.
+ *
+ * @param writer the writer to copy to
+ * @param reader the reader to copy from
+ * @param start_after #NULL or a reader showing where to start
+ * @param start_after_new_pos the position of start_after equivalent in the target data
+ * @param start_after_new_len the length of start_after equivalent in the target data
+ * @param fixups list to append #DBusArrayLenFixup if the write was partial
+ * @returns #FALSE if no memory
+ */
+static dbus_bool_t
+_dbus_type_writer_write_reader_partial (DBusTypeWriter *writer,
+ DBusTypeReader *reader,
+ const DBusTypeReader *start_after,
+ int start_after_new_pos,
+ int start_after_new_len,
+ DBusList **fixups)
+{
+ DBusTypeWriter orig;
+ int orig_type_len;
+ int orig_value_len;
+ int new_bytes;
+ int orig_enabled;
+
+ orig = *writer;
+ orig_type_len = _dbus_string_get_length (writer->type_str);
+ orig_value_len = _dbus_string_get_length (writer->value_str);
+ orig_enabled = writer->enabled;
+
+ if (start_after)
+ _dbus_type_writer_set_enabled (writer, FALSE);
+
+ if (!writer_write_reader_helper (writer, reader, start_after,
+ start_after_new_pos,
+ start_after_new_len,
+ fixups, FALSE))
+ goto oom;
+
+ _dbus_type_writer_set_enabled (writer, orig_enabled);
+ return TRUE;
+
+ oom:
+ if (!writer->type_pos_is_expectation)
+ {
+ new_bytes = _dbus_string_get_length (writer->type_str) - orig_type_len;
+ _dbus_string_delete (writer->type_str, orig.type_pos, new_bytes);
+ }
+ new_bytes = _dbus_string_get_length (writer->value_str) - orig_value_len;
+ _dbus_string_delete (writer->value_str, orig.value_pos, new_bytes);
+
+ *writer = orig;
+
+ return FALSE;
+}
+
+/**
+ * Iterate through all values in the given reader, writing a copy of
+ * each value to the writer. The reader will be moved forward to its
+ * end position.
+ *
+ * @param writer the writer to copy to
+ * @param reader the reader to copy from
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_type_writer_write_reader (DBusTypeWriter *writer,
+ DBusTypeReader *reader)
+{
+ return _dbus_type_writer_write_reader_partial (writer, reader, NULL, 0, 0, NULL);
+}
+
+/*
+ * If disabled, a writer can still be iterated forward and recursed/unrecursed
+ * but won't write any values. Types will still be written unless the
+ * writer is a "values only" writer, because the writer needs access to
+ * a valid signature to be able to iterate.
+ *
+ * @param writer the type writer
+ * @param enabled #TRUE if values should be written
+ */
+static void
+_dbus_type_writer_set_enabled (DBusTypeWriter *writer,
+ dbus_bool_t enabled)
+{
+ writer->enabled = enabled != FALSE;
+}
+
+/** @} */ /* end of DBusMarshal group */
+
+/* tests in dbus-marshal-recursive-util.c */
diff --git a/src/3rdparty/libdbus/dbus/dbus-marshal-recursive.h b/src/3rdparty/libdbus/dbus/dbus-marshal-recursive.h
new file mode 100644
index 00000000..4aa61ba1
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-marshal-recursive.h
@@ -0,0 +1,203 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-marshal-recursive.h Marshalling routines for recursive types
+ *
+ * Copyright (C) 2004, 2005 Red Hat, Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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
+ *
+ */
+
+#ifndef DBUS_MARSHAL_RECURSIVE_H
+#define DBUS_MARSHAL_RECURSIVE_H
+
+#include <dbus/dbus-protocol.h>
+#include <dbus/dbus-list.h>
+
+typedef struct DBusTypeReader DBusTypeReader;
+typedef struct DBusTypeWriter DBusTypeWriter;
+typedef struct DBusTypeReaderClass DBusTypeReaderClass;
+typedef struct DBusArrayLenFixup DBusArrayLenFixup;
+
+/**
+ * The type reader is an iterator for reading values from a block of
+ * values.
+ */
+struct DBusTypeReader
+{
+ const DBusTypeReaderClass *klass; /**< the vtable for the reader */
+ const DBusString *type_str; /**< string containing signature of block */
+ const DBusString *value_str; /**< string containing values of block */
+
+ dbus_uint32_t byte_order : 8; /**< byte order of the block */
+
+ dbus_uint32_t finished : 1; /**< marks we're at end iterator for cases
+ * where we don't have another way to tell
+ */
+ dbus_uint32_t array_len_offset : 3; /**< bytes back from start_pos that len ends */
+ int type_pos; /**< current position in signature */
+ int value_pos; /**< current position in values */
+
+ union
+ {
+ struct {
+ int start_pos; /**< for array readers, the start of the array values */
+ } array;
+ } u; /**< class-specific data */
+};
+
+/**
+ * The type writer is an iterator for writing to a block of values.
+ */
+struct DBusTypeWriter
+{
+ DBusString *type_str; /**< where to write typecodes (or read type expectations) */
+ DBusString *value_str; /**< where to write values */
+ dbus_uint32_t byte_order : 8; /**< byte order to write values with */
+
+ dbus_uint32_t container_type : 8; /**< what are we inside? (e.g. struct, variant, array) */
+
+ dbus_uint32_t type_pos_is_expectation : 1; /**< type_pos can be either an insertion point for or an expected next type */
+
+ dbus_uint32_t enabled : 1; /**< whether to write values */
+
+ int type_pos; /**< current pos in type_str */
+ int value_pos; /**< next position to write */
+
+ union
+ {
+ struct {
+ int start_pos; /**< position of first element in the array */
+ int len_pos; /**< position of length of the array */
+ int element_type_pos; /**< position of array element type in type_str */
+ } array;
+ } u; /**< class-specific data */
+};
+
+/**
+ * When modifying an existing block of values, array lengths may need
+ * to be adjusted; those adjustments are described by this struct.
+ */
+struct DBusArrayLenFixup
+{
+ int len_pos_in_reader; /**< where the length was in the original block */
+ int new_len; /**< the new value of the length in the written-out block */
+};
+
+DBUS_PRIVATE_EXPORT
+void _dbus_type_reader_init (DBusTypeReader *reader,
+ int byte_order,
+ const DBusString *type_str,
+ int type_pos,
+ const DBusString *value_str,
+ int value_pos);
+DBUS_PRIVATE_EXPORT
+void _dbus_type_reader_init_types_only (DBusTypeReader *reader,
+ const DBusString *type_str,
+ int type_pos);
+DBUS_PRIVATE_EXPORT
+int _dbus_type_reader_get_current_type (const DBusTypeReader *reader);
+DBUS_PRIVATE_EXPORT
+int _dbus_type_reader_get_element_type (const DBusTypeReader *reader);
+DBUS_PRIVATE_EXPORT
+int _dbus_type_reader_get_value_pos (const DBusTypeReader *reader);
+DBUS_PRIVATE_EXPORT
+void _dbus_type_reader_read_basic (const DBusTypeReader *reader,
+ void *value);
+int _dbus_type_reader_get_array_length (const DBusTypeReader *reader);
+DBUS_PRIVATE_EXPORT
+void _dbus_type_reader_read_fixed_multi (const DBusTypeReader *reader,
+ const void **value,
+ int *n_elements);
+void _dbus_type_reader_read_raw (const DBusTypeReader *reader,
+ const unsigned char **value_location);
+DBUS_PRIVATE_EXPORT
+void _dbus_type_reader_recurse (DBusTypeReader *reader,
+ DBusTypeReader *subreader);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_type_reader_next (DBusTypeReader *reader);
+dbus_bool_t _dbus_type_reader_has_next (const DBusTypeReader *reader);
+DBUS_PRIVATE_EXPORT
+void _dbus_type_reader_get_signature (const DBusTypeReader *reader,
+ const DBusString **str_p,
+ int *start_p,
+ int *len_p);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_type_reader_set_basic (DBusTypeReader *reader,
+ const void *value,
+ const DBusTypeReader *realign_root);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_type_reader_delete (DBusTypeReader *reader,
+ const DBusTypeReader *realign_root);
+
+dbus_bool_t _dbus_type_reader_equal_values (const DBusTypeReader *lhs,
+ const DBusTypeReader *rhs);
+
+void _dbus_type_signature_next (const char *signature,
+ int *type_pos);
+
+DBUS_PRIVATE_EXPORT
+void _dbus_type_writer_init (DBusTypeWriter *writer,
+ int byte_order,
+ DBusString *type_str,
+ int type_pos,
+ DBusString *value_str,
+ int value_pos);
+void _dbus_type_writer_init_types_delayed (DBusTypeWriter *writer,
+ int byte_order,
+ DBusString *value_str,
+ int value_pos);
+void _dbus_type_writer_add_types (DBusTypeWriter *writer,
+ DBusString *type_str,
+ int type_pos);
+void _dbus_type_writer_remove_types (DBusTypeWriter *writer);
+DBUS_PRIVATE_EXPORT
+void _dbus_type_writer_init_values_only (DBusTypeWriter *writer,
+ int byte_order,
+ const DBusString *type_str,
+ int type_pos,
+ DBusString *value_str,
+ int value_pos);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_type_writer_write_basic (DBusTypeWriter *writer,
+ int type,
+ const void *value);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_type_writer_write_fixed_multi (DBusTypeWriter *writer,
+ int element_type,
+ const void *value,
+ int n_elements);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_type_writer_recurse (DBusTypeWriter *writer,
+ int container_type,
+ const DBusString *contained_type,
+ int contained_type_start,
+ DBusTypeWriter *sub);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_type_writer_unrecurse (DBusTypeWriter *writer,
+ DBusTypeWriter *sub);
+dbus_bool_t _dbus_type_writer_append_array (DBusTypeWriter *writer,
+ const DBusString *contained_type,
+ int contained_type_start,
+ DBusTypeWriter *sub);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_type_writer_write_reader (DBusTypeWriter *writer,
+ DBusTypeReader *reader);
+
+
+#endif /* DBUS_MARSHAL_RECURSIVE_H */
diff --git a/src/3rdparty/libdbus/dbus/dbus-marshal-validate.c b/src/3rdparty/libdbus/dbus/dbus-marshal-validate.c
new file mode 100644
index 00000000..53378b61
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-marshal-validate.c
@@ -0,0 +1,1296 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-marshal-validate.c Validation routines for marshaled data
+ *
+ * Copyright (C) 2005 Red Hat, Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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-validate.h"
+#include "dbus-marshal-recursive.h"
+#include "dbus-marshal-basic.h"
+#include "dbus-signature.h"
+#include "dbus-string.h"
+
+/**
+ * @addtogroup DBusMarshal
+ *
+ * @{
+ */
+
+/**
+ * Verifies that the range of type_str from type_pos to type_end is a
+ * valid signature. If this function returns #TRUE, it will be safe
+ * to iterate over the signature with a types-only #DBusTypeReader.
+ * The range passed in should NOT include the terminating
+ * nul/DBUS_TYPE_INVALID.
+ *
+ * @param type_str the string
+ * @param type_pos where the typecodes start
+ * @param len length of typecodes
+ * @returns #DBUS_VALID if valid, reason why invalid otherwise
+ */
+DBusValidity
+_dbus_validate_signature_with_reason (const DBusString *type_str,
+ int type_pos,
+ int len)
+{
+ const unsigned char *p;
+ const unsigned char *end;
+ int last;
+ int struct_depth;
+ int array_depth;
+ int dict_entry_depth;
+ DBusValidity result;
+
+ int element_count;
+ DBusList *element_count_stack;
+ char opened_brackets[DBUS_MAXIMUM_TYPE_RECURSION_DEPTH * 2 + 1] = { '\0' };
+ char last_bracket;
+
+ result = DBUS_VALID;
+ element_count_stack = NULL;
+
+ if (!_dbus_list_append (&element_count_stack, _DBUS_INT_TO_POINTER (0)))
+ {
+ result = DBUS_VALIDITY_UNKNOWN_OOM_ERROR;
+ goto out;
+ }
+
+ _dbus_assert (type_str != NULL);
+ _dbus_assert (type_pos < _DBUS_INT32_MAX - len);
+ _dbus_assert (len >= 0);
+ _dbus_assert (type_pos >= 0);
+
+ if (len > DBUS_MAXIMUM_SIGNATURE_LENGTH)
+ {
+ result = DBUS_INVALID_SIGNATURE_TOO_LONG;
+ goto out;
+ }
+
+ p = _dbus_string_get_const_udata_len (type_str, type_pos, 0);
+
+ end = _dbus_string_get_const_udata_len (type_str, type_pos + len, 0);
+ struct_depth = 0;
+ array_depth = 0;
+ dict_entry_depth = 0;
+ last = DBUS_TYPE_INVALID;
+
+ while (p != end)
+ {
+ _dbus_assert (struct_depth + dict_entry_depth >= 0);
+ _dbus_assert (struct_depth + dict_entry_depth < _DBUS_N_ELEMENTS (opened_brackets));
+ _dbus_assert (opened_brackets[struct_depth + dict_entry_depth] == '\0');
+
+ switch (*p)
+ {
+ case DBUS_TYPE_BYTE:
+ case DBUS_TYPE_BOOLEAN:
+ case DBUS_TYPE_INT16:
+ case DBUS_TYPE_UINT16:
+ case DBUS_TYPE_INT32:
+ case DBUS_TYPE_UINT32:
+ case DBUS_TYPE_UNIX_FD:
+ case DBUS_TYPE_INT64:
+ case DBUS_TYPE_UINT64:
+ case DBUS_TYPE_DOUBLE:
+ case DBUS_TYPE_STRING:
+ case DBUS_TYPE_OBJECT_PATH:
+ case DBUS_TYPE_SIGNATURE:
+ case DBUS_TYPE_VARIANT:
+ break;
+
+ case DBUS_TYPE_ARRAY:
+ array_depth += 1;
+ if (array_depth > DBUS_MAXIMUM_TYPE_RECURSION_DEPTH)
+ {
+ result = DBUS_INVALID_EXCEEDED_MAXIMUM_ARRAY_RECURSION;
+ goto out;
+ }
+ break;
+
+ case DBUS_STRUCT_BEGIN_CHAR:
+ struct_depth += 1;
+
+ if (struct_depth > DBUS_MAXIMUM_TYPE_RECURSION_DEPTH)
+ {
+ result = DBUS_INVALID_EXCEEDED_MAXIMUM_STRUCT_RECURSION;
+ goto out;
+ }
+
+ if (!_dbus_list_append (&element_count_stack,
+ _DBUS_INT_TO_POINTER (0)))
+ {
+ result = DBUS_VALIDITY_UNKNOWN_OOM_ERROR;
+ goto out;
+ }
+
+ _dbus_assert (struct_depth + dict_entry_depth >= 1);
+ _dbus_assert (struct_depth + dict_entry_depth < _DBUS_N_ELEMENTS (opened_brackets));
+ _dbus_assert (opened_brackets[struct_depth + dict_entry_depth - 1] == '\0');
+ opened_brackets[struct_depth + dict_entry_depth - 1] = DBUS_STRUCT_BEGIN_CHAR;
+ break;
+
+ case DBUS_STRUCT_END_CHAR:
+ if (struct_depth == 0)
+ {
+ result = DBUS_INVALID_STRUCT_ENDED_BUT_NOT_STARTED;
+ goto out;
+ }
+
+ if (last == DBUS_STRUCT_BEGIN_CHAR)
+ {
+ result = DBUS_INVALID_STRUCT_HAS_NO_FIELDS;
+ goto out;
+ }
+
+ _dbus_assert (struct_depth + dict_entry_depth >= 1);
+ _dbus_assert (struct_depth + dict_entry_depth < _DBUS_N_ELEMENTS (opened_brackets));
+ last_bracket = opened_brackets[struct_depth + dict_entry_depth - 1];
+
+ if (last_bracket != DBUS_STRUCT_BEGIN_CHAR)
+ {
+ result = DBUS_INVALID_STRUCT_ENDED_BUT_NOT_STARTED;
+ goto out;
+ }
+
+ _dbus_list_pop_last (&element_count_stack);
+
+ struct_depth -= 1;
+ opened_brackets[struct_depth + dict_entry_depth] = '\0';
+ break;
+
+ case DBUS_DICT_ENTRY_BEGIN_CHAR:
+ if (last != DBUS_TYPE_ARRAY)
+ {
+ result = DBUS_INVALID_DICT_ENTRY_NOT_INSIDE_ARRAY;
+ goto out;
+ }
+
+ dict_entry_depth += 1;
+
+ if (dict_entry_depth > DBUS_MAXIMUM_TYPE_RECURSION_DEPTH)
+ {
+ result = DBUS_INVALID_EXCEEDED_MAXIMUM_DICT_ENTRY_RECURSION;
+ goto out;
+ }
+
+ if (!_dbus_list_append (&element_count_stack,
+ _DBUS_INT_TO_POINTER (0)))
+ {
+ result = DBUS_VALIDITY_UNKNOWN_OOM_ERROR;
+ goto out;
+ }
+
+ _dbus_assert (struct_depth + dict_entry_depth >= 1);
+ _dbus_assert (struct_depth + dict_entry_depth < _DBUS_N_ELEMENTS (opened_brackets));
+ _dbus_assert (opened_brackets[struct_depth + dict_entry_depth - 1] == '\0');
+ opened_brackets[struct_depth + dict_entry_depth - 1] = DBUS_DICT_ENTRY_BEGIN_CHAR;
+ break;
+
+ case DBUS_DICT_ENTRY_END_CHAR:
+ if (dict_entry_depth == 0)
+ {
+ result = DBUS_INVALID_DICT_ENTRY_ENDED_BUT_NOT_STARTED;
+ goto out;
+ }
+
+ _dbus_assert (struct_depth + dict_entry_depth >= 1);
+ _dbus_assert (struct_depth + dict_entry_depth < _DBUS_N_ELEMENTS (opened_brackets));
+ last_bracket = opened_brackets[struct_depth + dict_entry_depth - 1];
+
+ if (last_bracket != DBUS_DICT_ENTRY_BEGIN_CHAR)
+ {
+ result = DBUS_INVALID_DICT_ENTRY_ENDED_BUT_NOT_STARTED;
+ goto out;
+ }
+
+ dict_entry_depth -= 1;
+ opened_brackets[struct_depth + dict_entry_depth] = '\0';
+
+ element_count =
+ _DBUS_POINTER_TO_INT (_dbus_list_pop_last (&element_count_stack));
+
+ if (element_count != 2)
+ {
+ if (element_count == 0)
+ result = DBUS_INVALID_DICT_ENTRY_HAS_NO_FIELDS;
+ else if (element_count == 1)
+ result = DBUS_INVALID_DICT_ENTRY_HAS_ONLY_ONE_FIELD;
+ else
+ result = DBUS_INVALID_DICT_ENTRY_HAS_TOO_MANY_FIELDS;
+
+ goto out;
+ }
+ break;
+
+ case DBUS_TYPE_STRUCT: /* doesn't appear in signatures */
+ case DBUS_TYPE_DICT_ENTRY: /* ditto */
+ default:
+ result = DBUS_INVALID_UNKNOWN_TYPECODE;
+ goto out;
+ }
+
+ if (*p != DBUS_TYPE_ARRAY &&
+ *p != DBUS_DICT_ENTRY_BEGIN_CHAR &&
+ *p != DBUS_STRUCT_BEGIN_CHAR)
+ {
+ element_count =
+ _DBUS_POINTER_TO_INT (_dbus_list_pop_last (&element_count_stack));
+
+ ++element_count;
+
+ if (!_dbus_list_append (&element_count_stack,
+ _DBUS_INT_TO_POINTER (element_count)))
+ {
+ result = DBUS_VALIDITY_UNKNOWN_OOM_ERROR;
+ goto out;
+ }
+ }
+
+ if (array_depth > 0)
+ {
+ if (*p == DBUS_TYPE_ARRAY && p != end)
+ {
+ const unsigned char *p1;
+ p1 = p + 1;
+ if (*p1 == DBUS_STRUCT_END_CHAR ||
+ *p1 == DBUS_DICT_ENTRY_END_CHAR)
+ {
+ result = DBUS_INVALID_MISSING_ARRAY_ELEMENT_TYPE;
+ goto out;
+ }
+ }
+ else
+ {
+ array_depth = 0;
+ }
+ }
+
+ if (last == DBUS_DICT_ENTRY_BEGIN_CHAR)
+ {
+ if (!(dbus_type_is_valid (*p) && dbus_type_is_basic (*p)))
+ {
+ result = DBUS_INVALID_DICT_KEY_MUST_BE_BASIC_TYPE;
+ goto out;
+ }
+ }
+
+ last = *p;
+ ++p;
+ }
+
+
+ if (array_depth > 0)
+ {
+ result = DBUS_INVALID_MISSING_ARRAY_ELEMENT_TYPE;
+ goto out;
+ }
+
+ if (struct_depth > 0)
+ {
+ result = DBUS_INVALID_STRUCT_STARTED_BUT_NOT_ENDED;
+ goto out;
+ }
+
+ if (dict_entry_depth > 0)
+ {
+ result = DBUS_INVALID_DICT_ENTRY_STARTED_BUT_NOT_ENDED;
+ goto out;
+ }
+
+ _dbus_assert (last != DBUS_TYPE_ARRAY);
+ _dbus_assert (last != DBUS_STRUCT_BEGIN_CHAR);
+ _dbus_assert (last != DBUS_DICT_ENTRY_BEGIN_CHAR);
+
+ result = DBUS_VALID;
+
+out:
+ _dbus_list_clear (&element_count_stack);
+ return result;
+}
+
+/* note: this function is also used to validate the header's values,
+ * since the header is a valid body with a particular signature.
+ */
+static DBusValidity
+validate_body_helper (DBusTypeReader *reader,
+ int byte_order,
+ dbus_bool_t walk_reader_to_end,
+ int total_depth,
+ const unsigned char *p,
+ const unsigned char *end,
+ const unsigned char **new_p)
+{
+ int current_type;
+
+ /* The spec allows arrays and structs to each nest 32, for total
+ * nesting of 2*32. We want to impose the same limit on "dynamic"
+ * value nesting (not visible in the signature) which is introduced
+ * by DBUS_TYPE_VARIANT.
+ */
+ if (total_depth > (DBUS_MAXIMUM_TYPE_RECURSION_DEPTH * 2))
+ {
+ return DBUS_INVALID_NESTED_TOO_DEEPLY;
+ }
+
+ while ((current_type = _dbus_type_reader_get_current_type (reader)) != DBUS_TYPE_INVALID)
+ {
+ const unsigned char *a;
+ int alignment;
+
+#if 0
+ _dbus_verbose (" validating value of type %s type reader %p type_pos %d p %p end %p %d remain\n",
+ _dbus_type_to_string (current_type), reader, reader->type_pos, p, end,
+ (int) (end - p));
+#endif
+
+ /* Guarantee that p has one byte to look at */
+ if (p == end)
+ return DBUS_INVALID_NOT_ENOUGH_DATA;
+
+ switch (current_type)
+ {
+ /* Special case of fixed-length types: every byte is valid */
+ case DBUS_TYPE_BYTE:
+ ++p;
+ break;
+
+ /* Multi-byte fixed-length types require padding to their alignment */
+ case DBUS_TYPE_BOOLEAN:
+ case DBUS_TYPE_INT16:
+ case DBUS_TYPE_UINT16:
+ case DBUS_TYPE_INT32:
+ case DBUS_TYPE_UINT32:
+ case DBUS_TYPE_UNIX_FD:
+ case DBUS_TYPE_INT64:
+ case DBUS_TYPE_UINT64:
+ case DBUS_TYPE_DOUBLE:
+ alignment = _dbus_type_get_alignment (current_type);
+ a = _DBUS_ALIGN_ADDRESS (p, alignment);
+ if (a >= end)
+ return DBUS_INVALID_NOT_ENOUGH_DATA;
+ while (p != a)
+ {
+ if (*p != '\0')
+ return DBUS_INVALID_ALIGNMENT_PADDING_NOT_NUL;
+ ++p;
+ }
+
+ if (current_type == DBUS_TYPE_BOOLEAN)
+ {
+ dbus_uint32_t v;
+
+ if (p + 4 > end)
+ return DBUS_INVALID_NOT_ENOUGH_DATA;
+
+ v = _dbus_unpack_uint32 (byte_order, p);
+
+ if (!(v == 0 || v == 1))
+ return DBUS_INVALID_BOOLEAN_NOT_ZERO_OR_ONE;
+ }
+
+ p += alignment;
+ break;
+
+ /* Types that start with a 4-byte length */
+ case DBUS_TYPE_ARRAY:
+ case DBUS_TYPE_STRING:
+ case DBUS_TYPE_OBJECT_PATH:
+ {
+ dbus_uint32_t claimed_len;
+
+ a = _DBUS_ALIGN_ADDRESS (p, 4);
+ if (a + 4 > end)
+ return DBUS_INVALID_NOT_ENOUGH_DATA;
+ while (p != a)
+ {
+ if (*p != '\0')
+ return DBUS_INVALID_ALIGNMENT_PADDING_NOT_NUL;
+ ++p;
+ }
+
+ claimed_len = _dbus_unpack_uint32 (byte_order, p);
+ p += 4;
+
+ /* p may now be == end */
+ _dbus_assert (p <= end);
+
+ /* Arrays have padding between the length and the first
+ * array item, if it's necessary for the array's element type.
+ * This padding is not counted as part of the length
+ * claimed_len. */
+ if (current_type == DBUS_TYPE_ARRAY)
+ {
+ int array_elem_type = _dbus_type_reader_get_element_type (reader);
+
+ if (!dbus_type_is_valid (array_elem_type))
+ {
+ return DBUS_INVALID_UNKNOWN_TYPECODE;
+ }
+
+ alignment = _dbus_type_get_alignment (array_elem_type);
+
+ a = _DBUS_ALIGN_ADDRESS (p, alignment);
+
+ /* a may now be == end */
+ if (a > end)
+ return DBUS_INVALID_NOT_ENOUGH_DATA;
+
+ while (p != a)
+ {
+ if (*p != '\0')
+ return DBUS_INVALID_ALIGNMENT_PADDING_NOT_NUL;
+ ++p;
+ }
+ }
+
+ if (claimed_len > (unsigned long) (end - p))
+ return DBUS_INVALID_LENGTH_OUT_OF_BOUNDS;
+
+ if (current_type == DBUS_TYPE_OBJECT_PATH)
+ {
+ DBusString str;
+ _dbus_string_init_const_len (&str, (const char *) p, claimed_len);
+ if (!_dbus_validate_path (&str, 0,
+ _dbus_string_get_length (&str)))
+ return DBUS_INVALID_BAD_PATH;
+
+ p += claimed_len;
+ }
+ else if (current_type == DBUS_TYPE_STRING)
+ {
+ DBusString str;
+ _dbus_string_init_const_len (&str, (const char *) p, claimed_len);
+ if (!_dbus_string_validate_utf8 (&str, 0,
+ _dbus_string_get_length (&str)))
+ return DBUS_INVALID_BAD_UTF8_IN_STRING;
+
+ p += claimed_len;
+ }
+ else if (current_type == DBUS_TYPE_ARRAY && claimed_len > 0)
+ {
+ DBusTypeReader sub;
+ DBusValidity validity;
+ const unsigned char *array_end;
+ int array_elem_type;
+
+ if (claimed_len > DBUS_MAXIMUM_ARRAY_LENGTH)
+ return DBUS_INVALID_ARRAY_LENGTH_EXCEEDS_MAXIMUM;
+
+ /* Remember that the reader is types only, so we can't
+ * use it to iterate over elements. It stays the same
+ * for all elements.
+ */
+ _dbus_type_reader_recurse (reader, &sub);
+
+ array_end = p + claimed_len;
+ /* We effectively already checked this, by checking that
+ * claimed_len <= (end - p) */
+ _dbus_assert (array_end <= end);
+
+ array_elem_type = _dbus_type_reader_get_element_type (reader);
+
+ /* avoid recursive call to validate_body_helper if this is an array
+ * of fixed-size elements
+ */
+ if (dbus_type_is_fixed (array_elem_type))
+ {
+ /* Note that fixed-size types all have sizes equal to
+ * their alignments, so this is really the item size. */
+ alignment = _dbus_type_get_alignment (array_elem_type);
+ _dbus_assert (alignment == 1 || alignment == 2 ||
+ alignment == 4 || alignment == 8);
+
+ /* Because the alignment is a power of 2, this is
+ * equivalent to: (claimed_len % alignment) != 0,
+ * but avoids slower integer division */
+ if ((claimed_len & (alignment - 1)) != 0)
+ return DBUS_INVALID_ARRAY_LENGTH_INCORRECT;
+
+ /* bools need to be handled differently, because they can
+ * have an invalid value
+ */
+ if (array_elem_type == DBUS_TYPE_BOOLEAN)
+ {
+ dbus_uint32_t v;
+
+ while (p < array_end)
+ {
+ v = _dbus_unpack_uint32 (byte_order, p);
+
+ if (!(v == 0 || v == 1))
+ return DBUS_INVALID_BOOLEAN_NOT_ZERO_OR_ONE;
+
+ p += alignment;
+ }
+ }
+
+ else
+ {
+ p = array_end;
+ }
+ }
+
+ else
+ {
+ while (p < array_end)
+ {
+ validity = validate_body_helper (&sub, byte_order, FALSE,
+ total_depth + 1,
+ p, end, &p);
+ if (validity != DBUS_VALID)
+ return validity;
+ }
+ }
+
+ if (p != array_end)
+ return DBUS_INVALID_ARRAY_LENGTH_INCORRECT;
+ }
+
+ /* check nul termination */
+ if (current_type != DBUS_TYPE_ARRAY)
+ {
+ if (p == end)
+ return DBUS_INVALID_NOT_ENOUGH_DATA;
+
+ if (*p != '\0')
+ return DBUS_INVALID_STRING_MISSING_NUL;
+ ++p;
+ }
+ }
+ break;
+
+ case DBUS_TYPE_SIGNATURE:
+ {
+ dbus_uint32_t claimed_len;
+ DBusString str;
+ DBusValidity validity;
+
+ claimed_len = *p;
+ ++p;
+
+ /* 1 is for nul termination */
+ if (claimed_len + 1 > (unsigned long) (end - p))
+ return DBUS_INVALID_SIGNATURE_LENGTH_OUT_OF_BOUNDS;
+
+ _dbus_string_init_const_len (&str, (const char *) p, claimed_len);
+ validity =
+ _dbus_validate_signature_with_reason (&str, 0,
+ _dbus_string_get_length (&str));
+
+ if (validity != DBUS_VALID)
+ return validity;
+
+ p += claimed_len;
+
+ _dbus_assert (p < end);
+ if (*p != DBUS_TYPE_INVALID)
+ return DBUS_INVALID_SIGNATURE_MISSING_NUL;
+
+ ++p;
+
+ _dbus_verbose ("p = %p end = %p claimed_len %u\n", p, end, claimed_len);
+ }
+ break;
+
+ case DBUS_TYPE_VARIANT:
+ {
+ /* 1 byte sig len, sig typecodes, align to
+ * contained-type-boundary, values.
+ */
+
+ /* In addition to normal signature validation, we need to be sure
+ * the signature contains only a single (possibly container) type.
+ */
+ dbus_uint32_t claimed_len;
+ DBusString sig;
+ DBusTypeReader sub;
+ DBusValidity validity;
+ int contained_alignment;
+ int contained_type;
+ DBusValidity reason;
+
+ claimed_len = *p;
+ ++p;
+
+ /* + 1 for nul */
+ if (claimed_len + 1 > (unsigned long) (end - p))
+ return DBUS_INVALID_VARIANT_SIGNATURE_LENGTH_OUT_OF_BOUNDS;
+
+ _dbus_string_init_const_len (&sig, (const char *) p, claimed_len);
+ reason = _dbus_validate_signature_with_reason (&sig, 0,
+ _dbus_string_get_length (&sig));
+ if (!(reason == DBUS_VALID))
+ {
+ if (reason == DBUS_VALIDITY_UNKNOWN_OOM_ERROR)
+ return reason;
+ else
+ return DBUS_INVALID_VARIANT_SIGNATURE_BAD;
+ }
+
+ p += claimed_len;
+
+ if (*p != DBUS_TYPE_INVALID)
+ return DBUS_INVALID_VARIANT_SIGNATURE_MISSING_NUL;
+ ++p;
+
+ contained_type = _dbus_first_type_in_signature (&sig, 0);
+ if (contained_type == DBUS_TYPE_INVALID)
+ return DBUS_INVALID_VARIANT_SIGNATURE_EMPTY;
+
+ contained_alignment = _dbus_type_get_alignment (contained_type);
+
+ a = _DBUS_ALIGN_ADDRESS (p, contained_alignment);
+ if (a > end)
+ return DBUS_INVALID_NOT_ENOUGH_DATA;
+ while (p != a)
+ {
+ if (*p != '\0')
+ return DBUS_INVALID_ALIGNMENT_PADDING_NOT_NUL;
+ ++p;
+ }
+
+ _dbus_type_reader_init_types_only (&sub, &sig, 0);
+
+ _dbus_assert (_dbus_type_reader_get_current_type (&sub) != DBUS_TYPE_INVALID);
+
+ validity = validate_body_helper (&sub, byte_order, FALSE,
+ total_depth + 1,
+ p, end, &p);
+ if (validity != DBUS_VALID)
+ return validity;
+
+ if (_dbus_type_reader_next (&sub))
+ return DBUS_INVALID_VARIANT_SIGNATURE_SPECIFIES_MULTIPLE_VALUES;
+
+ _dbus_assert (_dbus_type_reader_get_current_type (&sub) == DBUS_TYPE_INVALID);
+ }
+ break;
+
+ case DBUS_TYPE_DICT_ENTRY:
+ case DBUS_TYPE_STRUCT:
+ {
+ DBusTypeReader sub;
+ DBusValidity validity;
+
+ a = _DBUS_ALIGN_ADDRESS (p, 8);
+ if (a > end)
+ return DBUS_INVALID_NOT_ENOUGH_DATA;
+ while (p != a)
+ {
+ if (*p != '\0')
+ return DBUS_INVALID_ALIGNMENT_PADDING_NOT_NUL;
+ ++p;
+ }
+
+ _dbus_type_reader_recurse (reader, &sub);
+
+ validity = validate_body_helper (&sub, byte_order, TRUE,
+ total_depth + 1,
+ p, end, &p);
+ if (validity != DBUS_VALID)
+ return validity;
+ }
+ break;
+
+ default:
+ _dbus_assert_not_reached ("invalid typecode in supposedly-validated signature");
+ break;
+ }
+
+#if 0
+ _dbus_verbose (" validated value of type %s type reader %p type_pos %d p %p end %p %d remain\n",
+ _dbus_type_to_string (current_type), reader, reader->type_pos, p, end,
+ (int) (end - p));
+#endif
+
+ if (p > end)
+ {
+ _dbus_verbose ("not enough data!!! p = %p end = %p end-p = %d\n",
+ p, end, (int) (end - p));
+ return DBUS_INVALID_NOT_ENOUGH_DATA;
+ }
+
+ if (walk_reader_to_end)
+ _dbus_type_reader_next (reader);
+ else
+ break;
+ }
+
+ if (new_p)
+ *new_p = p;
+
+ return DBUS_VALID;
+}
+
+/**
+ * Verifies that the range of value_str from value_pos to value_end is
+ * a legitimate value of type expected_signature. If this function
+ * returns #TRUE, it will be safe to iterate over the values with
+ * #DBusTypeReader. The signature is assumed to be already valid.
+ *
+ * If bytes_remaining is not #NULL, then leftover bytes will be stored
+ * there and #DBUS_VALID returned. If it is #NULL, then
+ * #DBUS_INVALID_TOO_MUCH_DATA will be returned if bytes are left
+ * over.
+ *
+ * @param expected_signature the expected types in the value_str
+ * @param expected_signature_start where in expected_signature is the signature
+ * @param byte_order the byte order
+ * @param bytes_remaining place to store leftover bytes
+ * @param value_str the string containing the body
+ * @param value_pos where the values start
+ * @param len length of values after value_pos
+ * @returns #DBUS_VALID if valid, reason why invalid otherwise
+ */
+DBusValidity
+_dbus_validate_body_with_reason (const DBusString *expected_signature,
+ int expected_signature_start,
+ int byte_order,
+ int *bytes_remaining,
+ const DBusString *value_str,
+ int value_pos,
+ int len)
+{
+ DBusTypeReader reader;
+ const unsigned char *p;
+ const unsigned char *end;
+ DBusValidity validity;
+
+ _dbus_assert (len >= 0);
+ _dbus_assert (value_pos >= 0);
+ _dbus_assert (value_pos <= _dbus_string_get_length (value_str) - len);
+
+ _dbus_verbose ("validating body from pos %d len %d sig '%s'\n",
+ value_pos, len, _dbus_string_get_const_data_len (expected_signature,
+ expected_signature_start,
+ 0));
+
+ _dbus_type_reader_init_types_only (&reader,
+ expected_signature, expected_signature_start);
+
+ p = _dbus_string_get_const_udata_len (value_str, value_pos, len);
+ end = p + len;
+
+ validity = validate_body_helper (&reader, byte_order, TRUE, 0, p, end, &p);
+ if (validity != DBUS_VALID)
+ return validity;
+
+ if (bytes_remaining)
+ {
+ *bytes_remaining = end - p;
+ return DBUS_VALID;
+ }
+ else if (p < end)
+ return DBUS_INVALID_TOO_MUCH_DATA;
+ else
+ {
+ _dbus_assert (p == end);
+ return DBUS_VALID;
+ }
+}
+
+/**
+ * Determine wether the given character is valid as the first character
+ * in a name.
+ */
+#define VALID_INITIAL_NAME_CHARACTER(c) \
+ ( ((c) >= 'A' && (c) <= 'Z') || \
+ ((c) >= 'a' && (c) <= 'z') || \
+ ((c) == '_') )
+
+/**
+ * Determine wether the given character is valid as a second or later
+ * character in a name
+ */
+#define VALID_NAME_CHARACTER(c) \
+ ( ((c) >= '0' && (c) <= '9') || \
+ ((c) >= 'A' && (c) <= 'Z') || \
+ ((c) >= 'a' && (c) <= 'z') || \
+ ((c) == '_') )
+
+/**
+ * Checks that the given range of the string is a valid object path
+ * name in the D-Bus protocol. Part of the validation ensures that
+ * the object path contains only ASCII.
+ *
+ * @todo this is inconsistent with most of DBusString in that
+ * it allows a start,len range that extends past the string end.
+ *
+ * @todo change spec to disallow more things, such as spaces in the
+ * path name
+ *
+ * @param str the string
+ * @param start first byte index to check
+ * @param len number of bytes to check
+ * @returns #TRUE if the byte range exists and is a valid name
+ */
+dbus_bool_t
+_dbus_validate_path (const DBusString *str,
+ int start,
+ int len)
+{
+ const unsigned char *s;
+ const unsigned char *end;
+ const unsigned char *last_slash;
+
+ _dbus_assert (start >= 0);
+ _dbus_assert (len >= 0);
+ _dbus_assert (start <= _dbus_string_get_length (str));
+
+ if (len > _dbus_string_get_length (str) - start)
+ return FALSE;
+
+ if (len == 0)
+ return FALSE;
+
+ s = _dbus_string_get_const_udata (str) + start;
+ end = s + len;
+
+ if (*s != '/')
+ return FALSE;
+ last_slash = s;
+ ++s;
+
+ while (s != end)
+ {
+ if (*s == '/')
+ {
+ if ((s - last_slash) < 2)
+ return FALSE; /* no empty path components allowed */
+
+ last_slash = s;
+ }
+ else
+ {
+ if (_DBUS_UNLIKELY (!VALID_NAME_CHARACTER (*s)))
+ return FALSE;
+ }
+
+ ++s;
+ }
+
+ if ((end - last_slash) < 2 &&
+ len > 1)
+ return FALSE; /* trailing slash not allowed unless the string is "/" */
+
+ return TRUE;
+}
+
+const char *
+_dbus_validity_to_error_message (DBusValidity validity)
+{
+ switch (validity)
+ {
+ case DBUS_VALIDITY_UNKNOWN_OOM_ERROR: return "Out of memory";
+ case DBUS_INVALID_FOR_UNKNOWN_REASON: return "Unknown reason";
+ case DBUS_VALID_BUT_INCOMPLETE: return "Valid but incomplete";
+ case DBUS_VALIDITY_UNKNOWN: return "Validity unknown";
+ case DBUS_VALID: return "Valid";
+ case DBUS_INVALID_UNKNOWN_TYPECODE: return "Unknown typecode";
+ case DBUS_INVALID_MISSING_ARRAY_ELEMENT_TYPE: return "Missing array element type";
+ case DBUS_INVALID_SIGNATURE_TOO_LONG: return "Signature is too long";
+ case DBUS_INVALID_EXCEEDED_MAXIMUM_ARRAY_RECURSION: return "Exceeded maximum array recursion";
+ case DBUS_INVALID_EXCEEDED_MAXIMUM_STRUCT_RECURSION: return "Exceeded maximum struct recursion";
+ case DBUS_INVALID_STRUCT_ENDED_BUT_NOT_STARTED: return "Struct ended but not started";
+ case DBUS_INVALID_STRUCT_STARTED_BUT_NOT_ENDED: return "Struct started but not ended";
+ case DBUS_INVALID_STRUCT_HAS_NO_FIELDS: return "Struct has no fields";
+ case DBUS_INVALID_ALIGNMENT_PADDING_NOT_NUL: return "Alignment padding not null";
+ case DBUS_INVALID_BOOLEAN_NOT_ZERO_OR_ONE: return "Boolean is not zero or one";
+ case DBUS_INVALID_NOT_ENOUGH_DATA: return "Not enough data";
+ case DBUS_INVALID_TOO_MUCH_DATA: return "Too much data";
+ case DBUS_INVALID_BAD_BYTE_ORDER: return "Bad byte order";
+ case DBUS_INVALID_BAD_PROTOCOL_VERSION: return "Bad protocol version";
+ case DBUS_INVALID_BAD_MESSAGE_TYPE: return "Bad message type";
+ case DBUS_INVALID_BAD_SERIAL: return "Bad serial";
+ case DBUS_INVALID_INSANE_FIELDS_ARRAY_LENGTH: return "Insane fields array length";
+ case DBUS_INVALID_INSANE_BODY_LENGTH: return "Insane body length";
+ case DBUS_INVALID_MESSAGE_TOO_LONG: return "Message too long";
+ case DBUS_INVALID_HEADER_FIELD_CODE: return "Header field code";
+ case DBUS_INVALID_HEADER_FIELD_HAS_WRONG_TYPE: return "Header field has wrong type";
+ case DBUS_INVALID_USES_LOCAL_INTERFACE: return "Uses local interface";
+ case DBUS_INVALID_USES_LOCAL_PATH: return "Uses local path";
+ case DBUS_INVALID_HEADER_FIELD_APPEARS_TWICE: return "Header field appears twice";
+ case DBUS_INVALID_BAD_DESTINATION: return "Bad destination";
+ case DBUS_INVALID_BAD_INTERFACE: return "Bad interface";
+ case DBUS_INVALID_BAD_MEMBER: return "Bad member";
+ case DBUS_INVALID_BAD_ERROR_NAME: return "Bad error name";
+ case DBUS_INVALID_BAD_SENDER: return "Bad sender";
+ case DBUS_INVALID_MISSING_PATH: return "Missing path";
+ case DBUS_INVALID_MISSING_INTERFACE: return "Missing interface";
+ case DBUS_INVALID_MISSING_MEMBER: return "Missing member";
+ case DBUS_INVALID_MISSING_ERROR_NAME: return "Missing error name";
+ case DBUS_INVALID_MISSING_REPLY_SERIAL: return "Missing reply serial";
+ case DBUS_INVALID_LENGTH_OUT_OF_BOUNDS: return "Length out of bounds";
+ case DBUS_INVALID_ARRAY_LENGTH_EXCEEDS_MAXIMUM: return "Array length exceeds maximum";
+ case DBUS_INVALID_BAD_PATH: return "Bad path";
+ case DBUS_INVALID_SIGNATURE_LENGTH_OUT_OF_BOUNDS: return "Signature length out of bounds";
+ case DBUS_INVALID_BAD_UTF8_IN_STRING: return "Bad utf8 in string";
+ case DBUS_INVALID_ARRAY_LENGTH_INCORRECT: return "Array length incorrect";
+ case DBUS_INVALID_VARIANT_SIGNATURE_LENGTH_OUT_OF_BOUNDS: return "Variant signature length out of bounds";
+ case DBUS_INVALID_VARIANT_SIGNATURE_BAD: return "Variant signature bad";
+ case DBUS_INVALID_VARIANT_SIGNATURE_EMPTY: return "Variant signature empty";
+ case DBUS_INVALID_VARIANT_SIGNATURE_SPECIFIES_MULTIPLE_VALUES: return "Variant signature specifies multiple values";
+ case DBUS_INVALID_VARIANT_SIGNATURE_MISSING_NUL: return "Variant signature missing nul";
+ case DBUS_INVALID_STRING_MISSING_NUL: return "String missing nul";
+ case DBUS_INVALID_SIGNATURE_MISSING_NUL: return "Signature missing nul";
+ case DBUS_INVALID_EXCEEDED_MAXIMUM_DICT_ENTRY_RECURSION: return "Exceeded maximum dict entry recursion";
+ case DBUS_INVALID_DICT_ENTRY_ENDED_BUT_NOT_STARTED: return "Dict entry ended but not started";
+ case DBUS_INVALID_DICT_ENTRY_STARTED_BUT_NOT_ENDED: return "Dict entry started but not ended";
+ case DBUS_INVALID_DICT_ENTRY_HAS_NO_FIELDS: return "Dict entry has no fields";
+ case DBUS_INVALID_DICT_ENTRY_HAS_ONLY_ONE_FIELD: return "Dict entry has only one field";
+ case DBUS_INVALID_DICT_ENTRY_HAS_TOO_MANY_FIELDS: return "Dict entry has too many fields";
+ case DBUS_INVALID_DICT_ENTRY_NOT_INSIDE_ARRAY: return "Dict entry not inside array";
+ case DBUS_INVALID_DICT_KEY_MUST_BE_BASIC_TYPE: return "Dict key must be basic type";
+ case DBUS_INVALID_MISSING_UNIX_FDS: return "Unix file descriptor missing";
+ case DBUS_INVALID_NESTED_TOO_DEEPLY: return "Variants cannot be used to create a hugely recursive tree of values";
+ case DBUS_VALIDITY_LAST:
+ default:
+ return "Invalid";
+ }
+}
+
+/**
+ * Checks that the given range of the string is a valid interface name
+ * in the D-Bus protocol. This includes a length restriction and an
+ * ASCII subset, see the specification.
+ *
+ * @todo this is inconsistent with most of DBusString in that
+ * it allows a start,len range that extends past the string end.
+ *
+ * @param str the string
+ * @param start first byte index to check
+ * @param len number of bytes to check
+ * @returns #TRUE if the byte range exists and is a valid name
+ */
+dbus_bool_t
+_dbus_validate_interface (const DBusString *str,
+ int start,
+ int len)
+{
+ const unsigned char *s;
+ const unsigned char *end;
+ const unsigned char *iface;
+ const unsigned char *last_dot;
+
+ _dbus_assert (start >= 0);
+ _dbus_assert (len >= 0);
+ _dbus_assert (start <= _dbus_string_get_length (str));
+
+ if (len > _dbus_string_get_length (str) - start)
+ return FALSE;
+
+ if (len > DBUS_MAXIMUM_NAME_LENGTH)
+ return FALSE;
+
+ if (len == 0)
+ return FALSE;
+
+ last_dot = NULL;
+ iface = _dbus_string_get_const_udata (str) + start;
+ end = iface + len;
+ s = iface;
+
+ /* check special cases of first char so it doesn't have to be done
+ * in the loop. Note we know len > 0
+ */
+ if (_DBUS_UNLIKELY (*s == '.')) /* disallow starting with a . */
+ return FALSE;
+ else if (_DBUS_UNLIKELY (!VALID_INITIAL_NAME_CHARACTER (*s)))
+ return FALSE;
+ else
+ ++s;
+
+ while (s != end)
+ {
+ if (*s == '.')
+ {
+ if (_DBUS_UNLIKELY ((s + 1) == end))
+ return FALSE;
+ else if (_DBUS_UNLIKELY (!VALID_INITIAL_NAME_CHARACTER (*(s + 1))))
+ return FALSE;
+ last_dot = s;
+ ++s; /* we just validated the next char, so skip two */
+ }
+ else if (_DBUS_UNLIKELY (!VALID_NAME_CHARACTER (*s)))
+ {
+ return FALSE;
+ }
+
+ ++s;
+ }
+
+ if (_DBUS_UNLIKELY (last_dot == NULL))
+ return FALSE;
+
+ return TRUE;
+}
+
+/**
+ * Checks that the given range of the string is a valid member name
+ * in the D-Bus protocol. This includes a length restriction, etc.,
+ * see the specification.
+ *
+ * @todo this is inconsistent with most of DBusString in that
+ * it allows a start,len range that extends past the string end.
+ *
+ * @param str the string
+ * @param start first byte index to check
+ * @param len number of bytes to check
+ * @returns #TRUE if the byte range exists and is a valid name
+ */
+dbus_bool_t
+_dbus_validate_member (const DBusString *str,
+ int start,
+ int len)
+{
+ const unsigned char *s;
+ const unsigned char *end;
+ const unsigned char *member;
+
+ _dbus_assert (start >= 0);
+ _dbus_assert (len >= 0);
+ _dbus_assert (start <= _dbus_string_get_length (str));
+
+ if (len > _dbus_string_get_length (str) - start)
+ return FALSE;
+
+ if (len > DBUS_MAXIMUM_NAME_LENGTH)
+ return FALSE;
+
+ if (len == 0)
+ return FALSE;
+
+ member = _dbus_string_get_const_udata (str) + start;
+ end = member + len;
+ s = member;
+
+ /* check special cases of first char so it doesn't have to be done
+ * in the loop. Note we know len > 0
+ */
+
+ if (_DBUS_UNLIKELY (!VALID_INITIAL_NAME_CHARACTER (*s)))
+ return FALSE;
+ else
+ ++s;
+
+ while (s != end)
+ {
+ if (_DBUS_UNLIKELY (!VALID_NAME_CHARACTER (*s)))
+ {
+ return FALSE;
+ }
+
+ ++s;
+ }
+
+ return TRUE;
+}
+
+/**
+ * Checks that the given range of the string is a valid error name
+ * in the D-Bus protocol. This includes a length restriction, etc.,
+ * see the specification.
+ *
+ * @todo this is inconsistent with most of DBusString in that
+ * it allows a start,len range that extends past the string end.
+ *
+ * @param str the string
+ * @param start first byte index to check
+ * @param len number of bytes to check
+ * @returns #TRUE if the byte range exists and is a valid name
+ */
+dbus_bool_t
+_dbus_validate_error_name (const DBusString *str,
+ int start,
+ int len)
+{
+ /* Same restrictions as interface name at the moment */
+ return _dbus_validate_interface (str, start, len);
+}
+
+/**
+ * Determine wether the given character is valid as the first character
+ * in a bus name.
+ */
+#define VALID_INITIAL_BUS_NAME_CHARACTER(c) \
+ ( ((c) >= 'A' && (c) <= 'Z') || \
+ ((c) >= 'a' && (c) <= 'z') || \
+ ((c) == '_') || ((c) == '-'))
+
+/**
+ * Determine wether the given character is valid as a second or later
+ * character in a bus name
+ */
+#define VALID_BUS_NAME_CHARACTER(c) \
+ ( ((c) >= '0' && (c) <= '9') || \
+ ((c) >= 'A' && (c) <= 'Z') || \
+ ((c) >= 'a' && (c) <= 'z') || \
+ ((c) == '_') || ((c) == '-'))
+
+static dbus_bool_t
+_dbus_validate_bus_name_full (const DBusString *str,
+ int start,
+ int len,
+ dbus_bool_t is_namespace)
+{
+ const unsigned char *s;
+ const unsigned char *end;
+ const unsigned char *iface;
+ const unsigned char *last_dot;
+
+ _dbus_assert (start >= 0);
+ _dbus_assert (len >= 0);
+ _dbus_assert (start <= _dbus_string_get_length (str));
+
+ if (len > _dbus_string_get_length (str) - start)
+ return FALSE;
+
+ if (len > DBUS_MAXIMUM_NAME_LENGTH)
+ return FALSE;
+
+ if (len == 0)
+ return FALSE;
+
+ last_dot = NULL;
+ iface = _dbus_string_get_const_udata (str) + start;
+ end = iface + len;
+ s = iface;
+
+ /* check special cases of first char so it doesn't have to be done
+ * in the loop. Note we know len > 0
+ */
+ if (*s == ':')
+ {
+ /* unique name */
+ ++s;
+ while (s != end)
+ {
+ if (*s == '.')
+ {
+ if (_DBUS_UNLIKELY ((s + 1) == end))
+ return FALSE;
+ if (_DBUS_UNLIKELY (!VALID_BUS_NAME_CHARACTER (*(s + 1))))
+ return FALSE;
+ ++s; /* we just validated the next char, so skip two */
+ }
+ else if (_DBUS_UNLIKELY (!VALID_BUS_NAME_CHARACTER (*s)))
+ {
+ return FALSE;
+ }
+
+ ++s;
+ }
+
+ return TRUE;
+ }
+ else if (_DBUS_UNLIKELY (*s == '.')) /* disallow starting with a . */
+ return FALSE;
+ else if (_DBUS_UNLIKELY (!VALID_INITIAL_BUS_NAME_CHARACTER (*s)))
+ return FALSE;
+ else
+ ++s;
+
+ while (s != end)
+ {
+ if (*s == '.')
+ {
+ if (_DBUS_UNLIKELY ((s + 1) == end))
+ return FALSE;
+ else if (_DBUS_UNLIKELY (!VALID_INITIAL_BUS_NAME_CHARACTER (*(s + 1))))
+ return FALSE;
+ last_dot = s;
+ ++s; /* we just validated the next char, so skip two */
+ }
+ else if (_DBUS_UNLIKELY (!VALID_BUS_NAME_CHARACTER (*s)))
+ {
+ return FALSE;
+ }
+
+ ++s;
+ }
+
+ if (!is_namespace && _DBUS_UNLIKELY (last_dot == NULL))
+ return FALSE;
+
+ return TRUE;
+}
+
+/**
+ * Checks that the given range of the string is a valid bus name in
+ * the D-Bus protocol. This includes a length restriction, etc., see
+ * the specification.
+ *
+ * @todo this is inconsistent with most of DBusString in that
+ * it allows a start,len range that extends past the string end.
+ *
+ * @param str the string
+ * @param start first byte index to check
+ * @param len number of bytes to check
+ * @returns #TRUE if the byte range exists and is a valid name
+ */
+dbus_bool_t
+_dbus_validate_bus_name (const DBusString *str,
+ int start,
+ int len)
+{
+ return _dbus_validate_bus_name_full (str, start, len, FALSE);
+}
+
+/**
+ * Checks that the given range of the string is a prefix of a valid bus name in
+ * the D-Bus protocol. Unlike _dbus_validate_bus_name(), this accepts strings
+ * with only one period-separated component.
+ *
+ * @todo this is inconsistent with most of DBusString in that
+ * it allows a start,len range that extends past the string end.
+ *
+ * @param str the string
+ * @param start first byte index to check
+ * @param len number of bytes to check
+ * @returns #TRUE if the byte range exists and is a valid name
+ */
+dbus_bool_t
+_dbus_validate_bus_namespace (const DBusString *str,
+ int start,
+ int len)
+{
+ return _dbus_validate_bus_name_full (str, start, len, TRUE);
+}
+
+/** define _dbus_check_is_valid_path() */
+DEFINE_DBUS_NAME_CHECK(path)
+/** define _dbus_check_is_valid_interface() */
+DEFINE_DBUS_NAME_CHECK(interface)
+/** define _dbus_check_is_valid_member() */
+DEFINE_DBUS_NAME_CHECK(member)
+/** define _dbus_check_is_valid_error_name() */
+DEFINE_DBUS_NAME_CHECK(error_name)
+/** define _dbus_check_is_valid_bus_name() */
+DEFINE_DBUS_NAME_CHECK(bus_name)
+/** define _dbus_check_is_valid_utf8() */
+DEFINE_DBUS_NAME_CHECK(utf8)
+
+/** @} */
+
+/* tests in dbus-marshal-validate-util.c */
diff --git a/src/3rdparty/libdbus/dbus/dbus-marshal-validate.h b/src/3rdparty/libdbus/dbus/dbus-marshal-validate.h
new file mode 100644
index 00000000..94737872
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-marshal-validate.h
@@ -0,0 +1,213 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-marshal-validate.h Validation routines for marshaled data
+ *
+ * Copyright (C) 2005 Red Hat, Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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
+ *
+ */
+
+#ifndef DBUS_MARSHAL_VALIDATE_H
+#define DBUS_MARSHAL_VALIDATE_H
+
+#include <dbus/dbus-string.h>
+
+/**
+ * @addtogroup DBusMarshal
+ *
+ * @{
+ */
+
+/**
+ * This is used rather than a bool for high visibility
+ */
+typedef enum
+{
+ DBUS_VALIDATION_MODE_WE_TRUST_THIS_DATA_ABSOLUTELY,
+ DBUS_VALIDATION_MODE_DATA_IS_UNTRUSTED
+} DBusValidationMode;
+
+/**
+ * This is primarily used in unit testing, so we can verify that each
+ * invalid message is invalid for the expected reasons. Thus we really
+ * want a distinct enum value for every codepath leaving the validator
+ * functions. Enum values are specified manually for ease of debugging
+ * (so you can see the enum value given a printf)
+ */
+typedef enum
+{
+#define _DBUS_NEGATIVE_VALIDITY_COUNT 4
+ DBUS_VALIDITY_UNKNOWN_OOM_ERROR = -4, /**< can't determine validity due to OOM */
+ DBUS_INVALID_FOR_UNKNOWN_REASON = -3,
+ DBUS_VALID_BUT_INCOMPLETE = -2,
+ DBUS_VALIDITY_UNKNOWN = -1,
+ DBUS_VALID = 0, /**< the data is valid */
+ DBUS_INVALID_UNKNOWN_TYPECODE = 1,
+ DBUS_INVALID_MISSING_ARRAY_ELEMENT_TYPE = 2,
+ DBUS_INVALID_SIGNATURE_TOO_LONG = 3, /* this one is impossible right now since
+ * you can't put a too-long value in a byte
+ */
+ DBUS_INVALID_EXCEEDED_MAXIMUM_ARRAY_RECURSION = 4,
+ DBUS_INVALID_EXCEEDED_MAXIMUM_STRUCT_RECURSION = 5,
+ DBUS_INVALID_STRUCT_ENDED_BUT_NOT_STARTED = 6,
+ DBUS_INVALID_STRUCT_STARTED_BUT_NOT_ENDED = 7,
+ DBUS_INVALID_STRUCT_HAS_NO_FIELDS = 8,
+ DBUS_INVALID_ALIGNMENT_PADDING_NOT_NUL = 9,
+ DBUS_INVALID_BOOLEAN_NOT_ZERO_OR_ONE = 10,
+ DBUS_INVALID_NOT_ENOUGH_DATA = 11,
+ DBUS_INVALID_TOO_MUCH_DATA = 12, /**< trailing junk makes it invalid */
+ DBUS_INVALID_BAD_BYTE_ORDER = 13,
+ DBUS_INVALID_BAD_PROTOCOL_VERSION = 14,
+ DBUS_INVALID_BAD_MESSAGE_TYPE = 15,
+ DBUS_INVALID_BAD_SERIAL = 16,
+ DBUS_INVALID_INSANE_FIELDS_ARRAY_LENGTH = 17,
+ DBUS_INVALID_INSANE_BODY_LENGTH = 18,
+ DBUS_INVALID_MESSAGE_TOO_LONG = 19,
+ DBUS_INVALID_HEADER_FIELD_CODE = 20,
+ DBUS_INVALID_HEADER_FIELD_HAS_WRONG_TYPE = 21,
+ DBUS_INVALID_USES_LOCAL_INTERFACE = 22,
+ DBUS_INVALID_USES_LOCAL_PATH = 23,
+ DBUS_INVALID_HEADER_FIELD_APPEARS_TWICE = 24,
+ DBUS_INVALID_BAD_DESTINATION = 25,
+ DBUS_INVALID_BAD_INTERFACE = 26,
+ DBUS_INVALID_BAD_MEMBER = 27,
+ DBUS_INVALID_BAD_ERROR_NAME = 28,
+ DBUS_INVALID_BAD_SENDER = 29,
+ DBUS_INVALID_MISSING_PATH = 30,
+ DBUS_INVALID_MISSING_INTERFACE = 31,
+ DBUS_INVALID_MISSING_MEMBER = 32,
+ DBUS_INVALID_MISSING_ERROR_NAME = 33,
+ DBUS_INVALID_MISSING_REPLY_SERIAL = 34,
+ DBUS_INVALID_LENGTH_OUT_OF_BOUNDS = 35,
+ DBUS_INVALID_ARRAY_LENGTH_EXCEEDS_MAXIMUM = 36,
+ DBUS_INVALID_BAD_PATH = 37,
+ DBUS_INVALID_SIGNATURE_LENGTH_OUT_OF_BOUNDS = 38,
+ DBUS_INVALID_BAD_UTF8_IN_STRING = 39,
+ DBUS_INVALID_ARRAY_LENGTH_INCORRECT = 40,
+ DBUS_INVALID_VARIANT_SIGNATURE_LENGTH_OUT_OF_BOUNDS = 41,
+ DBUS_INVALID_VARIANT_SIGNATURE_BAD = 42,
+ DBUS_INVALID_VARIANT_SIGNATURE_EMPTY = 43,
+ DBUS_INVALID_VARIANT_SIGNATURE_SPECIFIES_MULTIPLE_VALUES = 44,
+ DBUS_INVALID_VARIANT_SIGNATURE_MISSING_NUL = 45,
+ DBUS_INVALID_STRING_MISSING_NUL = 46,
+ DBUS_INVALID_SIGNATURE_MISSING_NUL = 47,
+ DBUS_INVALID_EXCEEDED_MAXIMUM_DICT_ENTRY_RECURSION = 48,
+ DBUS_INVALID_DICT_ENTRY_ENDED_BUT_NOT_STARTED = 49,
+ DBUS_INVALID_DICT_ENTRY_STARTED_BUT_NOT_ENDED = 50,
+ DBUS_INVALID_DICT_ENTRY_HAS_NO_FIELDS = 51,
+ DBUS_INVALID_DICT_ENTRY_HAS_ONLY_ONE_FIELD = 52,
+ DBUS_INVALID_DICT_ENTRY_HAS_TOO_MANY_FIELDS = 53,
+ DBUS_INVALID_DICT_ENTRY_NOT_INSIDE_ARRAY = 54,
+ DBUS_INVALID_DICT_KEY_MUST_BE_BASIC_TYPE = 55,
+ DBUS_INVALID_MISSING_UNIX_FDS = 56,
+ DBUS_INVALID_NESTED_TOO_DEEPLY = 57,
+ DBUS_VALIDITY_LAST
+} DBusValidity;
+
+DBUS_PRIVATE_EXPORT
+DBusValidity _dbus_validate_signature_with_reason (const DBusString *type_str,
+ int type_pos,
+ int len);
+DBUS_PRIVATE_EXPORT
+DBusValidity _dbus_validate_body_with_reason (const DBusString *expected_signature,
+ int expected_signature_start,
+ int byte_order,
+ int *bytes_remaining,
+ const DBusString *value_str,
+ int value_pos,
+ int len);
+
+const char *_dbus_validity_to_error_message (DBusValidity validity);
+
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_validate_path (const DBusString *str,
+ int start,
+ int len);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_validate_interface (const DBusString *str,
+ int start,
+ int len);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_validate_member (const DBusString *str,
+ int start,
+ int len);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_validate_error_name (const DBusString *str,
+ int start,
+ int len);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_validate_bus_name (const DBusString *str,
+ int start,
+ int len);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_validate_bus_namespace (const DBusString *str,
+ int start,
+ int len);
+/* just to have a name consistent with the above: */
+#define _dbus_validate_utf8(s,b,e) _dbus_string_validate_utf8 (s, b, e)
+
+#ifdef DBUS_DISABLE_CHECKS
+
+/* Be sure they don't exist, since we don't want to use them outside of checks
+ * and so we want the compile failure.
+ */
+#define DECLARE_DBUS_NAME_CHECK(what)
+#define DEFINE_DBUS_NAME_CHECK(what)
+
+#else /* !DBUS_DISABLE_CHECKS */
+
+/** A name check is used in _dbus_return_if_fail(), it's not suitable
+ * for validating untrusted data. use _dbus_validate_whatever for that.
+ */
+#define DECLARE_DBUS_NAME_CHECK(what) \
+dbus_bool_t _dbus_check_is_valid_##what (const char *name)
+
+/** Define a name check to be used in _dbus_return_if_fail() statements.
+ */
+#define DEFINE_DBUS_NAME_CHECK(what) \
+dbus_bool_t \
+_dbus_check_is_valid_##what (const char *name) \
+{ \
+ DBusString str; \
+ \
+ if (name == NULL) \
+ return FALSE; \
+ \
+ _dbus_string_init_const (&str, name); \
+ return _dbus_validate_##what (&str, 0, \
+ _dbus_string_get_length (&str)); \
+}
+#endif /* !DBUS_DISABLE_CHECKS */
+
+/** defines _dbus_check_is_valid_path() */
+DECLARE_DBUS_NAME_CHECK(path);
+/** defines _dbus_check_is_valid_interface() */
+DECLARE_DBUS_NAME_CHECK(interface);
+/** defines _dbus_check_is_valid_member() */
+DECLARE_DBUS_NAME_CHECK(member);
+/** defines _dbus_check_is_valid_error_name() */
+DECLARE_DBUS_NAME_CHECK(error_name);
+/** defines _dbus_check_is_valid_bus_name() */
+DECLARE_DBUS_NAME_CHECK(bus_name);
+/** defines _dbus_check_is_valid_utf8() */
+DECLARE_DBUS_NAME_CHECK(utf8);
+
+/** @} */
+
+#endif /* DBUS_MARSHAL_VALIDATE_H */
diff --git a/src/3rdparty/libdbus/dbus/dbus-memory.c b/src/3rdparty/libdbus/dbus/dbus-memory.c
new file mode 100644
index 00000000..be7e4ef6
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-memory.c
@@ -0,0 +1,952 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-memory.c D-Bus memory handling
+ *
+ * Copyright (C) 2002, 2003 Red Hat Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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-memory.h"
+#include "dbus-internals.h"
+#include "dbus-sysdeps.h"
+#include "dbus-list.h"
+#include "dbus-threads.h"
+#include <dbus/dbus-test-tap.h>
+#include <stdlib.h>
+
+/**
+ * @defgroup DBusMemory Memory Allocation
+ * @ingroup DBus
+ * @brief dbus_malloc(), dbus_free(), etc.
+ *
+ * Functions and macros related to allocating and releasing
+ * blocks of memory.
+ *
+ */
+
+/**
+ * @defgroup DBusMemoryInternals Memory allocation implementation details
+ * @ingroup DBusInternals
+ * @brief internals of dbus_malloc() etc.
+ *
+ * Implementation details related to allocating and releasing blocks
+ * of memory.
+ */
+
+/**
+ * @addtogroup DBusMemory
+ *
+ * @{
+ */
+
+/**
+ * @def dbus_new
+ *
+ * Safe macro for using dbus_malloc(). Accepts the type
+ * to allocate and the number of type instances to
+ * allocate as arguments, and returns a memory block
+ * cast to the desired type, instead of as a void*.
+ *
+ * @param type type name to allocate
+ * @param count number of instances in the allocated array
+ * @returns the new memory block or #NULL on failure
+ */
+
+/**
+ * @def dbus_new0
+ *
+ * Safe macro for using dbus_malloc0(). Accepts the type
+ * to allocate and the number of type instances to
+ * allocate as arguments, and returns a memory block
+ * cast to the desired type, instead of as a void*.
+ * The allocated array is initialized to all-bits-zero.
+ *
+ * @param type type name to allocate
+ * @param count number of instances in the allocated array
+ * @returns the new memory block or #NULL on failure
+ */
+
+/**
+ * @typedef DBusFreeFunction
+ *
+ * The type of a function which frees a block of memory.
+ *
+ * @param memory the memory to free
+ */
+
+/** @} */ /* end of public API docs */
+
+/**
+ * @addtogroup DBusMemoryInternals
+ *
+ * @{
+ */
+
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
+/* Test-only, does not need to be thread-safe */
+static dbus_bool_t debug_initialized = FALSE;
+static int fail_nth = -1;
+static size_t fail_size = 0;
+static int fail_alloc_counter = _DBUS_INT_MAX;
+static int n_failures_per_failure = 1;
+static int n_failures_this_failure = 0;
+static dbus_bool_t guards = FALSE;
+static dbus_bool_t disable_mem_pools = FALSE;
+static dbus_bool_t backtrace_on_fail_alloc = FALSE;
+static dbus_bool_t malloc_cannot_fail = FALSE;
+static DBusAtomic n_blocks_outstanding = {0};
+
+/** value stored in guard padding for debugging buffer overrun */
+#define GUARD_VALUE 0xdeadbeef
+/** size of the information about the block stored in guard mode */
+#define GUARD_INFO_SIZE 8
+/** size of the GUARD_VALUE-filled padding after the header info */
+#define GUARD_START_PAD 16
+/** size of the GUARD_VALUE-filled padding at the end of the block */
+#define GUARD_END_PAD 16
+/** size of stuff at start of block */
+#define GUARD_START_OFFSET (GUARD_START_PAD + GUARD_INFO_SIZE)
+/** total extra size over the requested allocation for guard stuff */
+#define GUARD_EXTRA_SIZE (GUARD_START_OFFSET + GUARD_END_PAD)
+
+static void
+_dbus_initialize_malloc_debug (void)
+{
+ if (!debug_initialized)
+ {
+ debug_initialized = TRUE;
+
+ if (_dbus_getenv ("DBUS_MALLOC_FAIL_NTH") != NULL)
+ {
+ fail_nth = atoi (_dbus_getenv ("DBUS_MALLOC_FAIL_NTH"));
+ fail_alloc_counter = fail_nth;
+ _dbus_verbose ("Will fail dbus_malloc every %d times\n", fail_nth);
+ }
+
+ if (_dbus_getenv ("DBUS_MALLOC_FAIL_GREATER_THAN") != NULL)
+ {
+ fail_size = atoi (_dbus_getenv ("DBUS_MALLOC_FAIL_GREATER_THAN"));
+ _dbus_verbose ("Will fail mallocs over %ld bytes\n",
+ (long) fail_size);
+ }
+
+ if (_dbus_getenv ("DBUS_MALLOC_GUARDS") != NULL)
+ {
+ guards = TRUE;
+ _dbus_verbose ("Will use dbus_malloc guards\n");
+ }
+
+ if (_dbus_getenv ("DBUS_DISABLE_MEM_POOLS") != NULL)
+ {
+ disable_mem_pools = TRUE;
+ _dbus_verbose ("Will disable memory pools\n");
+ }
+
+ if (_dbus_getenv ("DBUS_MALLOC_BACKTRACES") != NULL)
+ {
+ backtrace_on_fail_alloc = TRUE;
+ _dbus_verbose ("Will backtrace on failing a dbus_malloc\n");
+ }
+
+ if (_dbus_getenv ("DBUS_MALLOC_CANNOT_FAIL") != NULL)
+ {
+ malloc_cannot_fail = TRUE;
+ _dbus_verbose ("Will abort if system malloc() and friends fail\n");
+ }
+ }
+}
+
+/**
+ * Whether to turn off mem pools, useful for leak checking.
+ *
+ * @returns #TRUE if mempools should not be used.
+ */
+dbus_bool_t
+_dbus_disable_mem_pools (void)
+{
+ _dbus_initialize_malloc_debug ();
+ return disable_mem_pools;
+}
+
+/**
+ * Sets the number of allocations until we simulate a failed
+ * allocation. If set to 0, the next allocation to run
+ * fails; if set to 1, one succeeds then the next fails; etc.
+ * Set to _DBUS_INT_MAX to not fail anything.
+ *
+ * @param until_next_fail number of successful allocs before one fails
+ */
+void
+_dbus_set_fail_alloc_counter (int until_next_fail)
+{
+ _dbus_initialize_malloc_debug ();
+
+ fail_alloc_counter = until_next_fail;
+
+#if 0
+ _dbus_verbose ("Set fail alloc counter = %d\n", fail_alloc_counter);
+#endif
+}
+
+/**
+ * Gets the number of successful allocs until we'll simulate
+ * a failed alloc.
+ *
+ * @returns current counter value
+ */
+int
+_dbus_get_fail_alloc_counter (void)
+{
+ _dbus_initialize_malloc_debug ();
+
+ return fail_alloc_counter;
+}
+
+/**
+ * Sets how many mallocs to fail when the fail alloc counter reaches
+ * 0.
+ *
+ * @param failures_per_failure number to fail
+ */
+void
+_dbus_set_fail_alloc_failures (int failures_per_failure)
+{
+ n_failures_per_failure = failures_per_failure;
+}
+
+/**
+ * Gets the number of failures we'll have when the fail malloc
+ * counter reaches 0.
+ *
+ * @returns number of failures planned
+ */
+int
+_dbus_get_fail_alloc_failures (void)
+{
+ return n_failures_per_failure;
+}
+
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
+/**
+ * Called when about to alloc some memory; if
+ * it returns #TRUE, then the allocation should
+ * fail. If it returns #FALSE, then the allocation
+ * should not fail.
+ *
+ * @returns #TRUE if this alloc should fail
+ */
+dbus_bool_t
+_dbus_decrement_fail_alloc_counter (void)
+{
+ _dbus_initialize_malloc_debug ();
+
+ if (fail_alloc_counter <= 0)
+ {
+ if (backtrace_on_fail_alloc)
+ _dbus_print_backtrace ();
+
+ _dbus_verbose ("failure %d\n", n_failures_this_failure);
+
+ n_failures_this_failure += 1;
+ if (n_failures_this_failure >= n_failures_per_failure)
+ {
+ if (fail_nth >= 0)
+ fail_alloc_counter = fail_nth;
+ else
+ fail_alloc_counter = _DBUS_INT_MAX;
+
+ n_failures_this_failure = 0;
+
+ _dbus_verbose ("reset fail alloc counter to %d\n", fail_alloc_counter);
+ }
+
+ return TRUE;
+ }
+ else
+ {
+ fail_alloc_counter -= 1;
+ return FALSE;
+ }
+}
+#endif /* DBUS_ENABLE_EMBEDDED_TESTS */
+
+/**
+ * Get the number of outstanding malloc()'d blocks.
+ *
+ * @returns number of blocks
+ */
+int
+_dbus_get_malloc_blocks_outstanding (void)
+{
+ return _dbus_atomic_get (&n_blocks_outstanding);
+}
+
+/**
+ * Where the block came from.
+ */
+typedef enum
+{
+ SOURCE_UNKNOWN,
+ SOURCE_MALLOC,
+ SOURCE_REALLOC,
+ SOURCE_MALLOC_ZERO,
+ SOURCE_REALLOC_NULL
+} BlockSource;
+
+static const char*
+source_string (BlockSource source)
+{
+ switch (source)
+ {
+ case SOURCE_UNKNOWN:
+ return "unknown";
+ case SOURCE_MALLOC:
+ return "malloc";
+ case SOURCE_REALLOC:
+ return "realloc";
+ case SOURCE_MALLOC_ZERO:
+ return "malloc0";
+ case SOURCE_REALLOC_NULL:
+ return "realloc(NULL)";
+ default:
+ _dbus_assert_not_reached ("Invalid malloc block source ID");
+ return "invalid!";
+ }
+}
+
+static void
+check_guards (void *free_block,
+ dbus_bool_t overwrite)
+{
+ if (free_block != NULL)
+ {
+ unsigned char *block = ((unsigned char*)free_block) - GUARD_START_OFFSET;
+ size_t requested_bytes = *(dbus_uint32_t *) (void *) block;
+ BlockSource source = *(dbus_uint32_t *) (void *) (block + 4);
+ unsigned int i;
+ dbus_bool_t failed;
+
+ failed = FALSE;
+
+#if 0
+ _dbus_verbose ("Checking %d bytes request from source %s\n",
+ requested_bytes, source_string (source));
+#endif
+
+ i = GUARD_INFO_SIZE;
+ while (i < GUARD_START_OFFSET)
+ {
+ dbus_uint32_t value = *(dbus_uint32_t *) (void *) &block[i];
+ if (value != GUARD_VALUE)
+ {
+ _dbus_warn ("Block of %lu bytes from %s had start guard value 0x%ux at %d expected 0x%x",
+ (long) requested_bytes, source_string (source),
+ value, i, GUARD_VALUE);
+ failed = TRUE;
+ }
+
+ i += 4;
+ }
+
+ i = GUARD_START_OFFSET + requested_bytes;
+ while (i < (GUARD_START_OFFSET + requested_bytes + GUARD_END_PAD))
+ {
+ dbus_uint32_t value = *(dbus_uint32_t *) (void *) &block[i];
+ if (value != GUARD_VALUE)
+ {
+ _dbus_warn ("Block of %lu bytes from %s had end guard value 0x%ux at %d expected 0x%x",
+ (long) requested_bytes, source_string (source),
+ value, i, GUARD_VALUE);
+ failed = TRUE;
+ }
+
+ i += 4;
+ }
+
+ /* set memory to anything but nul bytes */
+ if (overwrite)
+ memset (free_block, 'g', requested_bytes);
+
+ if (failed)
+ _dbus_assert_not_reached ("guard value corruption");
+ }
+}
+
+static void*
+set_guards (void *real_block,
+ size_t requested_bytes,
+ BlockSource source)
+{
+ unsigned char *block = real_block;
+ unsigned int i;
+
+ if (block == NULL)
+ return NULL;
+
+ _dbus_assert (GUARD_START_OFFSET + GUARD_END_PAD == GUARD_EXTRA_SIZE);
+
+ *((dbus_uint32_t *) (void *) block) = requested_bytes;
+ *((dbus_uint32_t *) (void *) (block + 4)) = source;
+
+ i = GUARD_INFO_SIZE;
+ while (i < GUARD_START_OFFSET)
+ {
+ (*(dbus_uint32_t *) (void *) &block[i]) = GUARD_VALUE;
+
+ i += 4;
+ }
+
+ i = GUARD_START_OFFSET + requested_bytes;
+ while (i < (GUARD_START_OFFSET + requested_bytes + GUARD_END_PAD))
+ {
+ (*(dbus_uint32_t *) (void *) &block[i]) = GUARD_VALUE;
+
+ i += 4;
+ }
+
+ check_guards (block + GUARD_START_OFFSET, FALSE);
+
+ return block + GUARD_START_OFFSET;
+}
+
+#endif
+
+/** @} */ /* End of internals docs */
+
+
+/**
+ * @addtogroup DBusMemory
+ *
+ * @{
+ */
+
+/**
+ * Allocates the given number of bytes, as with standard
+ * malloc(). Guaranteed to return #NULL if bytes is zero
+ * on all platforms. Returns #NULL if the allocation fails.
+ * The memory must be released with dbus_free().
+ *
+ * dbus_malloc() memory is NOT safe to free with regular free() from
+ * the C library. Free it with dbus_free() only.
+ *
+ * @param bytes number of bytes to allocate
+ * @return allocated memory, or #NULL if the allocation fails.
+ */
+void*
+dbus_malloc (size_t bytes)
+{
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
+ _dbus_initialize_malloc_debug ();
+
+ if (_dbus_decrement_fail_alloc_counter ())
+ {
+ _dbus_verbose (" FAILING malloc of %ld bytes\n", (long) bytes);
+ return NULL;
+ }
+#endif
+
+ if (bytes == 0) /* some system mallocs handle this, some don't */
+ return NULL;
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
+ else if (fail_size != 0 && bytes > fail_size)
+ return NULL;
+ else if (guards)
+ {
+ void *block;
+
+ block = malloc (bytes + GUARD_EXTRA_SIZE);
+ if (block)
+ {
+ _dbus_atomic_inc (&n_blocks_outstanding);
+ }
+ else if (malloc_cannot_fail)
+ {
+ _dbus_warn ("out of memory: malloc (%ld + %ld)",
+ (long) bytes, (long) GUARD_EXTRA_SIZE);
+ _dbus_abort ();
+ }
+
+ return set_guards (block, bytes, SOURCE_MALLOC);
+ }
+#endif
+ else
+ {
+ void *mem;
+ mem = malloc (bytes);
+
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
+ if (mem)
+ {
+ _dbus_atomic_inc (&n_blocks_outstanding);
+ }
+ else if (malloc_cannot_fail)
+ {
+ _dbus_warn ("out of memory: malloc (%ld)", (long) bytes);
+ _dbus_abort ();
+ }
+#endif
+
+ return mem;
+ }
+}
+
+/**
+ * Allocates the given number of bytes, as with standard malloc(), but
+ * all bytes are initialized to zero as with calloc(). Guaranteed to
+ * return #NULL if bytes is zero on all platforms. Returns #NULL if the
+ * allocation fails. The memory must be released with dbus_free().
+ *
+ * dbus_malloc0() memory is NOT safe to free with regular free() from
+ * the C library. Free it with dbus_free() only.
+ *
+ * @param bytes number of bytes to allocate
+ * @return allocated memory, or #NULL if the allocation fails.
+ */
+void*
+dbus_malloc0 (size_t bytes)
+{
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
+ _dbus_initialize_malloc_debug ();
+
+ if (_dbus_decrement_fail_alloc_counter ())
+ {
+ _dbus_verbose (" FAILING malloc0 of %ld bytes\n", (long) bytes);
+
+ return NULL;
+ }
+#endif
+
+ if (bytes == 0)
+ return NULL;
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
+ else if (fail_size != 0 && bytes > fail_size)
+ return NULL;
+ else if (guards)
+ {
+ void *block;
+
+ block = calloc (bytes + GUARD_EXTRA_SIZE, 1);
+
+ if (block)
+ {
+ _dbus_atomic_inc (&n_blocks_outstanding);
+ }
+ else if (malloc_cannot_fail)
+ {
+ _dbus_warn ("out of memory: calloc (%ld + %ld, 1)",
+ (long) bytes, (long) GUARD_EXTRA_SIZE);
+ _dbus_abort ();
+ }
+
+ return set_guards (block, bytes, SOURCE_MALLOC_ZERO);
+ }
+#endif
+ else
+ {
+ void *mem;
+ mem = calloc (bytes, 1);
+
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
+ if (mem)
+ {
+ _dbus_atomic_inc (&n_blocks_outstanding);
+ }
+ else if (malloc_cannot_fail)
+ {
+ _dbus_warn ("out of memory: calloc (%ld)", (long) bytes);
+ _dbus_abort ();
+ }
+#endif
+
+ return mem;
+ }
+}
+
+/**
+ * Resizes a block of memory previously allocated by dbus_malloc() or
+ * dbus_malloc0(). Guaranteed to free the memory and return #NULL if bytes
+ * is zero on all platforms. Returns #NULL if the resize fails.
+ * If the resize fails, the memory is not freed.
+ *
+ * @param memory block to be resized
+ * @param bytes new size of the memory block
+ * @return allocated memory, or #NULL if the resize fails.
+ */
+void*
+dbus_realloc (void *memory,
+ size_t bytes)
+{
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
+ _dbus_initialize_malloc_debug ();
+
+ if (_dbus_decrement_fail_alloc_counter ())
+ {
+ _dbus_verbose (" FAILING realloc of %ld bytes\n", (long) bytes);
+
+ return NULL;
+ }
+#endif
+
+ if (bytes == 0) /* guarantee this is safe */
+ {
+ dbus_free (memory);
+ return NULL;
+ }
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
+ else if (fail_size != 0 && bytes > fail_size)
+ return NULL;
+ else if (guards)
+ {
+ if (memory)
+ {
+ size_t old_bytes;
+ void *block;
+
+ check_guards (memory, FALSE);
+
+ block = realloc (((unsigned char*)memory) - GUARD_START_OFFSET,
+ bytes + GUARD_EXTRA_SIZE);
+
+ if (block == NULL)
+ {
+ if (malloc_cannot_fail)
+ {
+ _dbus_warn ("out of memory: realloc (%p, %ld + %ld)",
+ memory, (long) bytes, (long) GUARD_EXTRA_SIZE);
+ _dbus_abort ();
+ }
+
+ return NULL;
+ }
+
+ old_bytes = *(dbus_uint32_t*)block;
+ if (bytes >= old_bytes)
+ /* old guards shouldn't have moved */
+ check_guards (((unsigned char*)block) + GUARD_START_OFFSET, FALSE);
+
+ return set_guards (block, bytes, SOURCE_REALLOC);
+ }
+ else
+ {
+ void *block;
+
+ block = malloc (bytes + GUARD_EXTRA_SIZE);
+
+ if (block)
+ {
+ _dbus_atomic_inc (&n_blocks_outstanding);
+ }
+ else if (malloc_cannot_fail)
+ {
+ _dbus_warn ("out of memory: malloc (%ld + %ld)",
+ (long) bytes, (long) GUARD_EXTRA_SIZE);
+ _dbus_abort ();
+ }
+
+ return set_guards (block, bytes, SOURCE_REALLOC_NULL);
+ }
+ }
+#endif
+ else
+ {
+ void *mem;
+ mem = realloc (memory, bytes);
+
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
+ if (mem == NULL && malloc_cannot_fail)
+ {
+ _dbus_warn ("out of memory: malloc (%ld)", (long) bytes);
+ _dbus_abort ();
+ }
+
+ if (memory == NULL && mem != NULL)
+ _dbus_atomic_inc (&n_blocks_outstanding);
+#endif
+ return mem;
+ }
+}
+
+/**
+ * Frees a block of memory previously allocated by dbus_malloc() or
+ * dbus_malloc0(). If passed #NULL, does nothing.
+ *
+ * @param memory block to be freed
+ */
+void
+dbus_free (void *memory)
+{
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
+ if (guards)
+ {
+ check_guards (memory, TRUE);
+ if (memory)
+ {
+#ifdef DBUS_DISABLE_ASSERT
+ _dbus_atomic_dec (&n_blocks_outstanding);
+#else
+ dbus_int32_t old_value;
+
+ old_value = _dbus_atomic_dec (&n_blocks_outstanding);
+ _dbus_assert (old_value >= 1);
+#endif
+
+ free (((unsigned char*)memory) - GUARD_START_OFFSET);
+ }
+
+ return;
+ }
+#endif
+
+ if (memory) /* we guarantee it's safe to free (NULL) */
+ {
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
+#ifdef DBUS_DISABLE_ASSERT
+ _dbus_atomic_dec (&n_blocks_outstanding);
+#else
+ dbus_int32_t old_value;
+
+ old_value = _dbus_atomic_dec (&n_blocks_outstanding);
+ _dbus_assert (old_value >= 1);
+#endif
+#endif
+
+ free (memory);
+ }
+}
+
+/**
+ * Frees a #NULL-terminated array of strings.
+ * If passed #NULL, does nothing.
+ *
+ * @param str_array the array to be freed
+ */
+void
+dbus_free_string_array (char **str_array)
+{
+ if (str_array)
+ {
+ int i;
+
+ i = 0;
+ while (str_array[i])
+ {
+ dbus_free (str_array[i]);
+ i++;
+ }
+
+ dbus_free (str_array);
+ }
+}
+
+/** @} */ /* End of public API docs block */
+
+
+/**
+ * @addtogroup DBusMemoryInternals
+ *
+ * @{
+ */
+
+/**
+ * _dbus_current_generation is used to track each
+ * time that dbus_shutdown() is called, so we can
+ * reinit things after it's been called. It is simply
+ * incremented each time we shut down.
+ */
+int _dbus_current_generation = 1;
+
+/**
+ * Represents a function to be called on shutdown.
+ */
+typedef struct ShutdownClosure ShutdownClosure;
+
+/**
+ * This struct represents a function to be called on shutdown.
+ */
+struct ShutdownClosure
+{
+ ShutdownClosure *next; /**< Next ShutdownClosure */
+ DBusShutdownFunction func; /**< Function to call */
+ void *data; /**< Data for function */
+};
+
+/* Protected by _DBUS_LOCK (shutdown_funcs) */
+static ShutdownClosure *registered_globals = NULL;
+
+/**
+ * Register a cleanup function to be called exactly once
+ * the next time dbus_shutdown() is called.
+ *
+ * @param func the function
+ * @param data data to pass to the function
+ * @returns #FALSE on not enough memory
+ */
+dbus_bool_t
+_dbus_register_shutdown_func (DBusShutdownFunction func,
+ void *data)
+{
+ dbus_bool_t ok;
+
+ if (!_DBUS_LOCK (shutdown_funcs))
+ return FALSE;
+
+ ok = _dbus_register_shutdown_func_unlocked (func, data);
+ _DBUS_UNLOCK (shutdown_funcs);
+ return ok;
+}
+
+dbus_bool_t
+_dbus_register_shutdown_func_unlocked (DBusShutdownFunction func,
+ void *data)
+{
+ ShutdownClosure *c;
+
+ c = dbus_new (ShutdownClosure, 1);
+
+ if (c == NULL)
+ return FALSE;
+
+ c->func = func;
+ c->data = data;
+
+ c->next = registered_globals;
+ registered_globals = c;
+
+ return TRUE;
+}
+
+/** @} */ /* End of private API docs block */
+
+
+/**
+ * @addtogroup DBusMemory
+ *
+ * @{
+ */
+
+/**
+ * Frees all memory allocated internally by libdbus and
+ * reverses the effects of dbus_threads_init(). libdbus keeps internal
+ * global variables, for example caches and thread locks, and it
+ * can be useful to free these internal data structures.
+ *
+ * dbus_shutdown() does NOT free memory that was returned
+ * to the application. It only frees libdbus-internal
+ * data structures.
+ *
+ * You MUST free all memory and release all reference counts
+ * returned to you by libdbus prior to calling dbus_shutdown().
+ *
+ * If a shared connection is open, calling dbus_shutdown() will
+ * drain its queue of messages and disconnect it. In particular,
+ * this will result in processing of the special Disconnected
+ * signal, which may result in a call to _exit(), unless you
+ * have used dbus_connection_set_exit_on_disconnect() to disable
+ * that behaviour.
+ *
+ * You can't continue to use any D-Bus objects, such as connections,
+ * that were allocated prior to dbus_shutdown(). You can, however,
+ * start over; call dbus_threads_init() again, create new connections,
+ * and so forth.
+ *
+ * WARNING: dbus_shutdown() is NOT thread safe, it must be called
+ * while NO other threads are using D-Bus. (Remember, you have to free
+ * all D-Bus objects and memory before you call dbus_shutdown(), so no
+ * thread can be using libdbus.)
+ *
+ * The purpose of dbus_shutdown() is to allow applications to get
+ * clean output from memory leak checkers. dbus_shutdown() may also be
+ * useful if you want to dlopen() libdbus instead of linking to it,
+ * and want to be able to unload the library again.
+ *
+ * There is absolutely no requirement to call dbus_shutdown() - in fact,
+ * most applications won't bother and should not feel guilty.
+ *
+ * You have to know that nobody is using libdbus in your application's
+ * process before you can call dbus_shutdown(). One implication of this
+ * is that calling dbus_shutdown() from a library is almost certainly
+ * wrong, since you don't know what the rest of the app is up to.
+ *
+ */
+void
+dbus_shutdown (void)
+{
+ while (registered_globals != NULL)
+ {
+ ShutdownClosure *c;
+
+ c = registered_globals;
+ registered_globals = c->next;
+
+ (* c->func) (c->data);
+
+ dbus_free (c);
+ }
+
+ /* We wrap this in the thread-initialization lock because
+ * dbus_threads_init() uses the current generation to tell whether
+ * we're initialized, so we need to make sure that un-initializing
+ * propagates into all threads. */
+ _dbus_threads_lock_platform_specific ();
+ _dbus_current_generation += 1;
+ _dbus_threads_unlock_platform_specific ();
+}
+
+/** @} */ /** End of public API docs block */
+
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
+#include "dbus-test.h"
+
+/**
+ * @ingroup DBusMemoryInternals
+ * Unit test for DBusMemory
+ * @returns #TRUE on success.
+ */
+dbus_bool_t
+_dbus_memory_test (const char *test_data_dir _DBUS_GNUC_UNUSED)
+{
+ dbus_bool_t old_guards;
+ void *p;
+ size_t size;
+
+ old_guards = guards;
+ guards = TRUE;
+ p = dbus_malloc (4);
+ if (p == NULL)
+ _dbus_test_fatal ("no memory");
+ for (size = 4; size < 256; size += 4)
+ {
+ p = dbus_realloc (p, size);
+ if (p == NULL)
+ _dbus_test_fatal ("no memory");
+ }
+ for (size = 256; size != 0; size -= 4)
+ {
+ p = dbus_realloc (p, size);
+ if (p == NULL)
+ _dbus_test_fatal ("no memory");
+ }
+ dbus_free (p);
+ guards = old_guards;
+ return TRUE;
+}
+
+#endif
diff --git a/src/3rdparty/libdbus/dbus/dbus-memory.h b/src/3rdparty/libdbus/dbus/dbus-memory.h
new file mode 100644
index 00000000..5b5a41e1
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-memory.h
@@ -0,0 +1,74 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-memory.h D-Bus memory handling
+ *
+ * Copyright (C) 2002 Red Hat Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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
+ *
+ */
+#if !defined (DBUS_INSIDE_DBUS_H) && !defined (DBUS_COMPILATION)
+#error "Only <dbus/dbus.h> can be included directly, this file may disappear or change contents."
+#endif
+
+#ifndef DBUS_MEMORY_H
+#define DBUS_MEMORY_H
+
+#include <dbus/dbus-macros.h>
+#include <stddef.h>
+
+DBUS_BEGIN_DECLS
+
+/**
+ * @addtogroup DBusMemory
+ * @{
+ */
+
+DBUS_EXPORT
+DBUS_MALLOC
+DBUS_ALLOC_SIZE(1)
+void* dbus_malloc (size_t bytes);
+
+DBUS_EXPORT
+DBUS_MALLOC
+DBUS_ALLOC_SIZE(1)
+void* dbus_malloc0 (size_t bytes);
+
+DBUS_EXPORT
+DBUS_ALLOC_SIZE(2)
+void* dbus_realloc (void *memory,
+ size_t bytes);
+DBUS_EXPORT
+void dbus_free (void *memory);
+
+#define dbus_new(type, count) ((type*)dbus_malloc (sizeof (type) * (count)))
+#define dbus_new0(type, count) ((type*)dbus_malloc0 (sizeof (type) * (count)))
+
+DBUS_EXPORT
+void dbus_free_string_array (char **str_array);
+
+typedef void (* DBusFreeFunction) (void *memory);
+
+DBUS_EXPORT
+void dbus_shutdown (void);
+
+/** @} */
+
+DBUS_END_DECLS
+
+#endif /* DBUS_MEMORY_H */
diff --git a/src/3rdparty/libdbus/dbus/dbus-mempool.c b/src/3rdparty/libdbus/dbus/dbus-mempool.c
new file mode 100644
index 00000000..5bc4a97b
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-mempool.c
@@ -0,0 +1,469 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-mempool.h Memory pools
+ *
+ * Copyright (C) 2002, 2003 Red Hat, Inc.
+ * Copyright (C) 2003 CodeFactory AB
+ * Copyright (C) 2011-2012 Collabora Ltd.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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-mempool.h"
+#include "dbus-internals.h"
+#include "dbus-valgrind-internal.h"
+
+/**
+ * @defgroup DBusMemPool memory pools
+ * @ingroup DBusInternals
+ * @brief DBusMemPool object
+ *
+ * Types and functions related to DBusMemPool. A memory pool is used
+ * to decrease memory fragmentation/overhead and increase speed for
+ * blocks of small uniformly-sized objects. The main point is to avoid
+ * the overhead of a malloc block for each small object, speed is
+ * secondary.
+ */
+
+/**
+ * @defgroup DBusMemPoolInternals Memory pool implementation details
+ * @ingroup DBusInternals
+ * @brief DBusMemPool implementation details
+ *
+ * The guts of DBusMemPool.
+ *
+ * @{
+ */
+
+/**
+ * typedef so DBusFreedElement struct can refer to itself.
+ */
+typedef struct DBusFreedElement DBusFreedElement;
+
+/**
+ * struct representing an element on the free list.
+ * We just cast freed elements to this so we can
+ * make a list out of them.
+ */
+struct DBusFreedElement
+{
+ DBusFreedElement *next; /**< next element of the free list */
+};
+
+/**
+ * Typedef for DBusMemBlock so the struct can recursively
+ * point to itself.
+ */
+typedef struct DBusMemBlock DBusMemBlock;
+
+/**
+ * DBusMemBlock object represents a single malloc()-returned
+ * block that gets chunked up into objects in the memory pool.
+ */
+struct DBusMemBlock
+{
+ DBusMemBlock *next; /**< next block in the list, which is already used up;
+ * only saved so we can free all the blocks
+ * when we free the mem pool.
+ */
+
+ size_t used_so_far; /**< bytes of this block already allocated as elements. */
+#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)
+ /*
+ * Ensure that elements is aligned correctly. For all supported pre-C11
+ * targets, the size_t above should ensure that the elements array is
+ * sufficiently aligned (this is checked in the static assert below).
+ */
+ _Alignas (dbus_max_align_t)
+#endif
+ unsigned char elements[]; /**< the block data, actually allocated to required size */
+};
+
+_DBUS_STATIC_ASSERT (_DBUS_IS_ALIGNED (sizeof (struct DBusMemBlock),
+ _DBUS_ALIGNOF (dbus_max_align_t)));
+_DBUS_STATIC_ASSERT (_DBUS_IS_ALIGNED (offsetof (struct DBusMemBlock,
+ elements),
+ _DBUS_ALIGNOF (dbus_max_align_t)));
+
+/**
+ * Internals fields of DBusMemPool
+ */
+struct DBusMemPool
+{
+ size_t element_size; /**< size of a single object in the pool */
+ size_t block_size; /**< size of most recently allocated block */
+ unsigned int zero_elements : 1; /**< whether to zero-init allocated elements */
+
+ DBusFreedElement *free_elements; /**< a free list of elements to recycle */
+ DBusMemBlock *blocks; /**< blocks of memory from malloc() */
+ int allocated_elements; /**< Count of outstanding allocated elements */
+};
+
+/** @} */
+
+/**
+ * @addtogroup DBusMemPool
+ *
+ * @{
+ */
+
+/**
+ * @typedef DBusMemPool
+ *
+ * Opaque object representing a memory pool. Memory pools allow
+ * avoiding per-malloc-block memory overhead when allocating a lot of
+ * small objects that are all the same size. They are slightly
+ * faster than calling malloc() also.
+ */
+
+/**
+ * Creates a new memory pool, or returns #NULL on failure. Objects in
+ * the pool must be at least sizeof(void*) bytes each, due to the way
+ * memory pools work. To avoid creating 64 bit problems, this means at
+ * least 8 bytes on all platforms, unless you are 4 bytes on 32-bit
+ * and 8 bytes on 64-bit.
+ *
+ * @param element_size size of an element allocated from the pool.
+ * @param zero_elements whether to zero-initialize elements
+ * @returns the new pool or #NULL
+ */
+DBusMemPool*
+_dbus_mem_pool_new (int element_size,
+ dbus_bool_t zero_elements)
+{
+ DBusMemPool *pool;
+
+ pool = dbus_new0 (DBusMemPool, 1);
+ if (pool == NULL)
+ return NULL;
+
+ /* Make the element size at least 8 bytes. */
+ if (element_size < 8)
+ element_size = 8;
+ if (element_size < (int) sizeof (void *))
+ element_size = sizeof (void *);
+
+ /* these assertions are equivalent but the first is more clear
+ * to programmers that see it fail.
+ */
+ _dbus_assert (element_size >= (int) sizeof (void*));
+ _dbus_assert (element_size >= (int) sizeof (DBusFreedElement));
+
+ /* align the element size to be suitable for the most-aligned type
+ * that we care about (in practice usually a pointer).
+ */
+ pool->element_size =
+ _DBUS_ALIGN_VALUE (element_size, _DBUS_ALIGNOF (dbus_max_align_t));
+
+ pool->zero_elements = zero_elements != FALSE;
+
+ pool->allocated_elements = 0;
+
+ /* pick a size for the first block; it increases
+ * for each block we need to allocate. This is
+ * actually half the initial block size
+ * since _dbus_mem_pool_alloc() unconditionally
+ * doubles it prior to creating a new block. */
+ pool->block_size = pool->element_size * 8;
+
+ _dbus_assert ((pool->block_size %
+ pool->element_size) == 0);
+
+ VALGRIND_CREATE_MEMPOOL (pool, 0, zero_elements);
+
+ return pool;
+}
+
+/**
+ * Frees a memory pool (and all elements allocated from it).
+ *
+ * @param pool the memory pool.
+ */
+void
+_dbus_mem_pool_free (DBusMemPool *pool)
+{
+ DBusMemBlock *block;
+
+ VALGRIND_DESTROY_MEMPOOL (pool);
+
+ block = pool->blocks;
+ while (block != NULL)
+ {
+ DBusMemBlock *next = block->next;
+
+ dbus_free (block);
+
+ block = next;
+ }
+
+ dbus_free (pool);
+}
+
+/**
+ * Allocates an object from the memory pool.
+ * The object must be freed with _dbus_mem_pool_dealloc().
+ *
+ * @param pool the memory pool
+ * @returns the allocated object or #NULL if no memory.
+ */
+void*
+_dbus_mem_pool_alloc (DBusMemPool *pool)
+{
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
+ if (_dbus_disable_mem_pools ())
+ {
+ DBusMemBlock *block;
+ size_t alloc_size;
+
+ /* This is obviously really silly, but it's
+ * debug-mode-only code that is compiled out
+ * when tests are disabled (_dbus_disable_mem_pools()
+ * is a constant expression FALSE so this block
+ * should vanish)
+ */
+
+ alloc_size = sizeof (DBusMemBlock) + pool->element_size;
+
+ if (pool->zero_elements)
+ block = dbus_malloc0 (alloc_size);
+ else
+ block = dbus_malloc (alloc_size);
+
+ if (block != NULL)
+ {
+ block->next = pool->blocks;
+ pool->blocks = block;
+ pool->allocated_elements += 1;
+
+ VALGRIND_MEMPOOL_ALLOC (pool, (void *) &block->elements[0],
+ pool->element_size);
+ _dbus_assert (_DBUS_IS_ALIGNED (&block->elements[0],
+ _DBUS_ALIGNOF (dbus_max_align_t)));
+ return (void*) &block->elements[0];
+ }
+ else
+ return NULL;
+ }
+ else
+#endif
+ {
+ if (_dbus_decrement_fail_alloc_counter ())
+ {
+ _dbus_verbose (" FAILING mempool alloc\n");
+ return NULL;
+ }
+ else if (pool->free_elements)
+ {
+ DBusFreedElement *element = pool->free_elements;
+
+ pool->free_elements = pool->free_elements->next;
+
+ VALGRIND_MEMPOOL_ALLOC (pool, element, pool->element_size);
+
+ if (pool->zero_elements)
+ memset (element, '\0', pool->element_size);
+
+ pool->allocated_elements += 1;
+ _dbus_assert (
+ _DBUS_IS_ALIGNED (element, _DBUS_ALIGNOF (dbus_max_align_t)));
+ return element;
+ }
+ else
+ {
+ void *element;
+
+ if (pool->blocks == NULL ||
+ pool->blocks->used_so_far == pool->block_size)
+ {
+ /* Need a new block */
+ DBusMemBlock *block;
+ size_t alloc_size;
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
+ int saved_counter;
+#endif
+
+ if (pool->block_size <= _DBUS_INT_MAX / 4) /* avoid overflow */
+ {
+ /* use a larger block size for our next block */
+ pool->block_size *= 2;
+ _dbus_assert ((pool->block_size %
+ pool->element_size) == 0);
+ }
+
+ alloc_size = sizeof (DBusMemBlock) + pool->block_size;
+
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
+ /* We save/restore the counter, so that memory pools won't
+ * cause a given function to have different number of
+ * allocations on different invocations. i.e. when testing
+ * we want consistent alloc patterns. So we skip our
+ * malloc here for purposes of failed alloc simulation.
+ */
+ saved_counter = _dbus_get_fail_alloc_counter ();
+ _dbus_set_fail_alloc_counter (_DBUS_INT_MAX);
+#endif
+
+ if (pool->zero_elements)
+ block = dbus_malloc0 (alloc_size);
+ else
+ block = dbus_malloc (alloc_size);
+ _dbus_assert (
+ _DBUS_IS_ALIGNED (block, _DBUS_ALIGNOF (dbus_max_align_t)));
+
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
+ _dbus_set_fail_alloc_counter (saved_counter);
+ _dbus_assert (saved_counter == _dbus_get_fail_alloc_counter ());
+#endif
+
+ if (block == NULL)
+ return NULL;
+
+ block->used_so_far = 0;
+ block->next = pool->blocks;
+ pool->blocks = block;
+ }
+
+ element = &pool->blocks->elements[pool->blocks->used_so_far];
+
+ pool->blocks->used_so_far += pool->element_size;
+
+ pool->allocated_elements += 1;
+
+ VALGRIND_MEMPOOL_ALLOC (pool, element, pool->element_size);
+ _dbus_assert (
+ _DBUS_IS_ALIGNED (element, _DBUS_ALIGNOF (dbus_max_align_t)));
+ return element;
+ }
+ }
+}
+
+/**
+ * Deallocates an object previously created with
+ * _dbus_mem_pool_alloc(). The previous object
+ * must have come from this same pool.
+ * @param pool the memory pool
+ * @param element the element earlier allocated.
+ * @returns #TRUE if there are no remaining allocated elements
+ */
+dbus_bool_t
+_dbus_mem_pool_dealloc (DBusMemPool *pool,
+ void *element)
+{
+ VALGRIND_MEMPOOL_FREE (pool, element);
+
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
+ if (_dbus_disable_mem_pools ())
+ {
+ DBusMemBlock *block;
+ DBusMemBlock *prev;
+
+ /* mmm, fast. ;-) debug-only code, so doesn't matter. */
+
+ prev = NULL;
+ block = pool->blocks;
+
+ while (block != NULL)
+ {
+ if (block->elements == (unsigned char*) element)
+ {
+ if (prev)
+ prev->next = block->next;
+ else
+ pool->blocks = block->next;
+
+ dbus_free (block);
+
+ _dbus_assert (pool->allocated_elements > 0);
+ pool->allocated_elements -= 1;
+
+ if (pool->allocated_elements == 0)
+ _dbus_assert (pool->blocks == NULL);
+
+ return pool->blocks == NULL;
+ }
+ prev = block;
+ block = block->next;
+ }
+
+ _dbus_assert_not_reached ("freed nonexistent block");
+ return FALSE;
+ }
+ else
+#endif
+ {
+ DBusFreedElement *freed;
+
+ freed = element;
+ /* used for internal mempool administration */
+ VALGRIND_MAKE_MEM_UNDEFINED (freed, sizeof (*freed));
+
+ freed->next = pool->free_elements;
+ pool->free_elements = freed;
+
+ _dbus_assert (pool->allocated_elements > 0);
+ pool->allocated_elements -= 1;
+
+ return pool->allocated_elements == 0;
+ }
+}
+
+#ifdef DBUS_ENABLE_STATS
+void
+_dbus_mem_pool_get_stats (DBusMemPool *pool,
+ dbus_uint32_t *in_use_p,
+ dbus_uint32_t *in_free_list_p,
+ dbus_uint32_t *allocated_p)
+{
+ DBusMemBlock *block;
+ DBusFreedElement *freed;
+ dbus_uint32_t in_use = 0;
+ dbus_uint32_t in_free_list = 0;
+ dbus_uint32_t allocated = 0;
+
+ if (pool != NULL)
+ {
+ in_use = pool->element_size * pool->allocated_elements;
+
+ for (freed = pool->free_elements; freed != NULL; freed = freed->next)
+ {
+ in_free_list += pool->element_size;
+ }
+
+ for (block = pool->blocks; block != NULL; block = block->next)
+ {
+ if (block == pool->blocks)
+ allocated += pool->block_size;
+ else
+ allocated += block->used_so_far;
+ }
+ }
+
+ if (in_use_p != NULL)
+ *in_use_p = in_use;
+
+ if (in_free_list_p != NULL)
+ *in_free_list_p = in_free_list;
+
+ if (allocated_p != NULL)
+ *allocated_p = allocated;
+}
+#endif /* DBUS_ENABLE_STATS */
+
+/** @} */
diff --git a/src/3rdparty/libdbus/dbus/dbus-mempool.h b/src/3rdparty/libdbus/dbus/dbus-mempool.h
new file mode 100644
index 00000000..fe7efd1d
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-mempool.h
@@ -0,0 +1,56 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-mempool.h Memory pools
+ *
+ * Copyright (C) 2002 Red Hat, Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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
+ *
+ */
+
+#ifndef DBUS_MEMPOOL_H
+#define DBUS_MEMPOOL_H
+
+#include <dbus/dbus-internals.h>
+#include <dbus/dbus-memory.h>
+#include <dbus/dbus-types.h>
+
+DBUS_BEGIN_DECLS
+
+typedef struct DBusMemPool DBusMemPool;
+
+DBUS_PRIVATE_EXPORT
+DBusMemPool* _dbus_mem_pool_new (int element_size,
+ dbus_bool_t zero_elements);
+DBUS_PRIVATE_EXPORT
+void _dbus_mem_pool_free (DBusMemPool *pool);
+DBUS_PRIVATE_EXPORT
+void* _dbus_mem_pool_alloc (DBusMemPool *pool);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_mem_pool_dealloc (DBusMemPool *pool,
+ void *element);
+
+/* if DBUS_ENABLE_STATS */
+void _dbus_mem_pool_get_stats (DBusMemPool *pool,
+ dbus_uint32_t *in_use_p,
+ dbus_uint32_t *in_free_list_p,
+ dbus_uint32_t *allocated_p);
+
+DBUS_END_DECLS
+
+#endif /* DBUS_MEMPOOL_H */
diff --git a/src/3rdparty/libdbus/dbus/dbus-message-internal.h b/src/3rdparty/libdbus/dbus/dbus-message-internal.h
new file mode 100644
index 00000000..55f473f4
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-message-internal.h
@@ -0,0 +1,150 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-message-internal.h DBusMessage object internal interfaces
+ *
+ * Copyright (C) 2002 Red Hat Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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
+ *
+ */
+#ifndef DBUS_MESSAGE_INTERNAL_H
+#define DBUS_MESSAGE_INTERNAL_H
+
+#include <dbus/dbus-marshal-validate.h>
+#include <dbus/dbus-message.h>
+#include <dbus/dbus-resources.h>
+#include <dbus/dbus-list.h>
+
+DBUS_BEGIN_DECLS
+
+#ifdef DBUS_ENABLE_VERBOSE_MODE
+void _dbus_message_trace_ref (DBusMessage *message,
+ int old_refcount,
+ int new_refcount,
+ const char *why);
+#else
+/* this bypasses any "unused" warnings for the old and new refcount */
+#define _dbus_message_trace_ref(m, o, n, w) \
+ do \
+ {\
+ (void) (o); \
+ (void) (n); \
+ } while (0)
+#endif
+
+typedef struct DBusMessageLoader DBusMessageLoader;
+
+void _dbus_message_get_network_data (DBusMessage *message,
+ const DBusString **header,
+ const DBusString **body);
+DBUS_PRIVATE_EXPORT
+void _dbus_message_get_unix_fds (DBusMessage *message,
+ const int **fds,
+ unsigned *n_fds);
+
+unsigned int _dbus_message_get_n_unix_fds (DBusMessage *message);
+void _dbus_message_lock (DBusMessage *message);
+void _dbus_message_unlock (DBusMessage *message);
+dbus_bool_t _dbus_message_add_counter (DBusMessage *message,
+ DBusCounter *counter);
+void _dbus_message_add_counter_link (DBusMessage *message,
+ DBusList *link);
+void _dbus_message_remove_counter (DBusMessage *message,
+ DBusCounter *counter);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_message_remove_unknown_fields (DBusMessage *message);
+
+DBUS_PRIVATE_EXPORT
+DBusMessageLoader* _dbus_message_loader_new (void);
+DBUS_PRIVATE_EXPORT
+DBusMessageLoader* _dbus_message_loader_ref (DBusMessageLoader *loader);
+DBUS_PRIVATE_EXPORT
+void _dbus_message_loader_unref (DBusMessageLoader *loader);
+
+DBUS_PRIVATE_EXPORT
+void _dbus_message_loader_get_buffer (DBusMessageLoader *loader,
+ DBusString **buffer,
+ int *max_to_read,
+ dbus_bool_t *may_read_unix_fds);
+DBUS_PRIVATE_EXPORT
+void _dbus_message_loader_return_buffer (DBusMessageLoader *loader,
+ DBusString *buffer);
+
+
+#ifdef HAVE_UNIX_FD_PASSING
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_message_loader_get_unix_fds (DBusMessageLoader *loader,
+ int **fds,
+ unsigned *max_n_fds);
+
+DBUS_PRIVATE_EXPORT
+void _dbus_message_loader_return_unix_fds (DBusMessageLoader *loader,
+ int *fds,
+ unsigned n_fds);
+#endif
+
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_message_loader_queue_messages (DBusMessageLoader *loader);
+DBusMessage* _dbus_message_loader_peek_message (DBusMessageLoader *loader);
+DBUS_PRIVATE_EXPORT
+DBusMessage* _dbus_message_loader_pop_message (DBusMessageLoader *loader);
+DBusList* _dbus_message_loader_pop_message_link (DBusMessageLoader *loader);
+void _dbus_message_loader_putback_message_link (DBusMessageLoader *loader,
+ DBusList *link);
+
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_message_loader_get_is_corrupted (DBusMessageLoader *loader);
+DBusValidity _dbus_message_loader_get_corruption_reason (DBusMessageLoader *loader);
+
+void _dbus_message_loader_set_max_message_size (DBusMessageLoader *loader,
+ long size);
+DBUS_PRIVATE_EXPORT
+long _dbus_message_loader_get_max_message_size (DBusMessageLoader *loader);
+
+void _dbus_message_loader_set_max_message_unix_fds(DBusMessageLoader *loader,
+ long n);
+long _dbus_message_loader_get_max_message_unix_fds(DBusMessageLoader *loader);
+int _dbus_message_loader_get_pending_fds_count (DBusMessageLoader *loader);
+void _dbus_message_loader_set_pending_fds_function (DBusMessageLoader *loader,
+ void (* callback) (void *),
+ void *data);
+
+typedef struct DBusVariant DBusVariant;
+DBUS_PRIVATE_EXPORT
+DBusVariant *_dbus_variant_read (DBusMessageIter *reader);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_variant_write (DBusVariant *self,
+ DBusMessageIter *writer);
+DBUS_PRIVATE_EXPORT
+void _dbus_variant_free (DBusVariant *self);
+DBUS_PRIVATE_EXPORT
+int _dbus_variant_get_length (DBusVariant *self);
+DBUS_PRIVATE_EXPORT
+const DBusString *_dbus_variant_peek (DBusVariant *self);
+DBUS_PRIVATE_EXPORT
+const char *_dbus_variant_get_signature (DBusVariant *self);
+
+static inline void
+_dbus_clear_variant (DBusVariant **variant_p)
+{
+ _dbus_clear_pointer_impl (DBusVariant, variant_p, _dbus_variant_free);
+}
+
+DBUS_END_DECLS
+
+#endif /* DBUS_MESSAGE_INTERNAL_H */
diff --git a/src/3rdparty/libdbus/dbus/dbus-message-private.h b/src/3rdparty/libdbus/dbus/dbus-message-private.h
new file mode 100644
index 00000000..eb52f44f
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-message-private.h
@@ -0,0 +1,148 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-message-private.h header shared between dbus-message.c and dbus-message-util.c
+ *
+ * Copyright (C) 2005 Red Hat Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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
+ *
+ */
+#ifndef DBUS_MESSAGE_PRIVATE_H
+#define DBUS_MESSAGE_PRIVATE_H
+
+#include <dbus/dbus-message.h>
+#include <dbus/dbus-message-internal.h>
+#include <dbus/dbus-string.h>
+#include <dbus/dbus-dataslot.h>
+#include <dbus/dbus-marshal-header.h>
+
+DBUS_BEGIN_DECLS
+
+/**
+ * @addtogroup DBusMessageInternals
+ * @{
+ */
+
+/**
+ * @typedef DBusMessageLoader
+ *
+ * The DBusMessageLoader object encapsulates the process of converting
+ * a byte stream into a series of DBusMessage. It buffers the incoming
+ * bytes as efficiently as possible, and generates a queue of
+ * messages. DBusMessageLoader is typically used as part of a
+ * DBusTransport implementation. The DBusTransport then hands off
+ * the loaded messages to a DBusConnection, making the messages
+ * visible to the application.
+ *
+ * @todo write tests for break-loader that a) randomly delete header
+ * fields and b) set string fields to zero-length and other funky
+ * values.
+ *
+ */
+
+/**
+ * Implementation details of DBusMessageLoader.
+ * All members are private.
+ */
+struct DBusMessageLoader
+{
+ int refcount; /**< Reference count. */
+
+ DBusString data; /**< Buffered data */
+
+ DBusList *messages; /**< Complete messages. */
+
+ long max_message_size; /**< Maximum size of a message */
+ long max_message_unix_fds; /**< Maximum unix fds in a message */
+
+ DBusValidity corruption_reason; /**< why we were corrupted */
+
+ unsigned int corrupted : 1; /**< We got broken data, and are no longer working */
+
+ unsigned int buffer_outstanding : 1; /**< Someone is using the buffer to read */
+
+#ifdef HAVE_UNIX_FD_PASSING
+ unsigned int unix_fds_outstanding : 1; /**< Someone is using the unix fd array to read */
+
+ int *unix_fds; /**< File descriptors that have been read from the transport but not yet been handed to any message. Array will be allocated at first use. */
+ unsigned n_unix_fds_allocated; /**< Number of file descriptors this array has space for */
+ unsigned n_unix_fds; /**< Number of valid file descriptors in array */
+ void (* unix_fds_change) (void *); /**< Notify when the pending fds change */
+ void *unix_fds_change_data;
+#endif
+};
+
+
+/** How many bits are in the changed_stamp used to validate iterators */
+#define CHANGED_STAMP_BITS 21
+
+/**
+ * @brief Internals of DBusMessage
+ *
+ * Object representing a message received from or to be sent to
+ * another application. This is an opaque object, all members
+ * are private.
+ */
+struct DBusMessage
+{
+ DBusAtomic refcount; /**< Reference count */
+
+ DBusHeader header; /**< Header network data and associated cache */
+
+ DBusString body; /**< Body network data. */
+
+ unsigned int locked : 1; /**< Message being sent, no modifications allowed. */
+
+#ifndef DBUS_DISABLE_CHECKS
+ unsigned int in_cache : 1; /**< Has been "freed" since it's in the cache (this is a debug feature) */
+#endif
+
+ DBusList *counters; /**< 0-N DBusCounter used to track message size/unix fds. */
+ long size_counter_delta; /**< Size we incremented the size counters by. */
+
+ dbus_uint32_t changed_stamp : CHANGED_STAMP_BITS; /**< Incremented when iterators are invalidated. */
+
+ DBusDataSlotList slot_list; /**< Data stored by allocated integer ID */
+
+#ifndef DBUS_DISABLE_CHECKS
+ int generation; /**< _dbus_current_generation when message was created */
+#endif
+
+#ifdef HAVE_UNIX_FD_PASSING
+ int *unix_fds;
+ /**< Unix file descriptors associated with this message. These are
+ closed when the message is destroyed, hence make sure to dup()
+ them when adding or removing them here. */
+ unsigned n_unix_fds; /**< Number of valid fds in the array */
+ unsigned n_unix_fds_allocated; /**< Allocated size of the array */
+
+ long unix_fd_counter_delta; /**< Size we incremented the unix fd counter by */
+#endif
+};
+
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_message_iter_get_args_valist (DBusMessageIter *iter,
+ DBusError *error,
+ int first_arg_type,
+ va_list var_args);
+
+/** @} */
+
+DBUS_END_DECLS
+
+#endif /* DBUS_MESSAGE_H */
diff --git a/src/3rdparty/libdbus/dbus/dbus-message.c b/src/3rdparty/libdbus/dbus/dbus-message.c
new file mode 100644
index 00000000..b47a8638
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-message.c
@@ -0,0 +1,5574 @@
+/* -*- 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
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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.h"
+#include "dbus-sysdeps-unix.h"
+#endif
+
+#include <string.h>
+
+#define _DBUS_TYPE_IS_STRINGLIKE(type) \
+ (type == DBUS_TYPE_STRING || type == DBUS_TYPE_SIGNATURE || \
+ type == DBUS_TYPE_OBJECT_PATH)
+
+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.
+ *
+ * @{
+ */
+
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
+static dbus_bool_t
+_dbus_enable_message_cache (void)
+{
+ static int enabled = -1;
+
+ if (enabled < 0)
+ {
+ const char *s = _dbus_getenv ("DBUS_MESSAGE_CACHE");
+
+ enabled = TRUE;
+
+ if (s && *s)
+ {
+ if (*s == '0')
+ enabled = FALSE;
+ else if (*s == '1')
+ enabled = TRUE;
+ else
+ _dbus_warn ("DBUS_MESSAGE_CACHE should be 0 or 1 if set, not '%s'",
+ s);
+ }
+ }
+
+ return enabled;
+}
+#else
+ /* constant expression, should be optimized away */
+# define _dbus_enable_message_cache() (TRUE)
+#endif
+
+#ifndef _dbus_message_trace_ref
+void
+_dbus_message_trace_ref (DBusMessage *message,
+ int old_refcount,
+ int new_refcount,
+ const char *why)
+{
+ static int enabled = -1;
+
+ _dbus_trace_ref ("DBusMessage", message, old_refcount, new_refcount, why,
+ "DBUS_MESSAGE_TRACE", &enabled);
+}
+#endif
+
+/* 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 */
+};
+
+#if DBUS_SIZEOF_VOID_P > 8
+/*
+ * Architectures with 128-bit pointers were not supported in DBus 1.10, so we
+ * do no check for DBus 1.10 structure layout compatibility for such
+ * architectures (e.g. Arm Morello).
+ */
+#define CHECK_DBUS_1_10_BINARY_COMPATIBILITY 0
+#else
+#define CHECK_DBUS_1_10_BINARY_COMPATIBILITY 1
+/**
+ * Layout of a DBusMessageIter on the stack in dbus 1.10.0. This is no
+ * longer used, but for ABI compatibility we need to assert that the
+ * new layout is the same size.
+ */
+typedef struct
+{
+ void *dummy1;
+ void *dummy2;
+ dbus_uint32_t dummy3;
+ int dummy4;
+ int dummy5;
+ int dummy6;
+ int dummy7;
+ int dummy8;
+ int dummy9;
+ int dummy10;
+ int dummy11;
+ int pad1;
+ int pad2;
+ void *pad3;
+} DBusMessageIter_1_10_0;
+#endif
+
+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;
+ char byte_order;
+
+ byte_order = _dbus_header_get_byte_order (&message->header);
+
+ if (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,
+ byte_order,
+ DBUS_COMPILER_BYTE_ORDER,
+ &message->body, 0);
+
+ _dbus_header_byteswap (&message->header, DBUS_COMPILER_BYTE_ORDER);
+ _dbus_assert (_dbus_header_get_byte_order (&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) _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
+}
+
+/**
+ * Remove every header field not known to this version of dbus.
+ *
+ * @param message the message
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_message_remove_unknown_fields (DBusMessage *message)
+{
+ return _dbus_header_remove_unknown_fields (&message->header);
+}
+
+/**
+ * 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.
+ *
+ * This function may be called with locks held. As a result, the counter's
+ * notify function is not called; the caller is expected to either call
+ * _dbus_counter_notify() on the counter when they are no longer holding
+ * locks, or take the same action that would be taken by the notify function.
+ *
+ * @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.
+ *
+ * This function may be called with locks held. As a result, the counter's
+ * notify function is not called; the caller is expected to either call
+ * _dbus_counter_notify() on the counter when they are no longer holding
+ * locks, or take the same action that would be taken by the notify function.
+ *
+ * @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 counter the counter
+ */
+void
+_dbus_message_remove_counter (DBusMessage *message,
+ DBusCounter *counter)
+{
+ DBusList *link;
+
+ link = _dbus_list_find_last (&message->counters,
+ counter);
+ _dbus_assert (link != NULL);
+
+ _dbus_list_remove_link (&message->counters, 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_notify (counter);
+ _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);
+}
+
+/* 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
+
+/* Protected by _DBUS_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;
+
+ if (!_DBUS_LOCK (message_cache))
+ _dbus_assert_not_reached ("we would have initialized global locks "
+ "before registering a shutdown function");
+
+ 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;
+
+ if (!_DBUS_LOCK (message_cache))
+ {
+ /* we'd have initialized global locks before caching anything,
+ * so there can't be anything in the cache */
+ return NULL;
+ }
+
+ _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 (_dbus_atomic_get (&message->refcount) == 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;
+ unsigned 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", 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_notify (counter);
+ _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 (_dbus_atomic_get (&message->refcount) == 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;
+
+ if (!_DBUS_LOCK (message_cache))
+ {
+ /* The only way to get a non-null message goes through
+ * dbus_message_get_cached() which takes the lock. */
+ _dbus_assert_not_reached ("we would have initialized global locks "
+ "the first time we constructed a message");
+ }
+
+ 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_enable_message_cache ())
+ goto out;
+
+ 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 (_dbus_atomic_get (&message->refcount) == 0);
+
+ _DBUS_UNLOCK (message_cache);
+
+ if (!was_cached)
+ dbus_message_finalize (message);
+}
+
+/*
+ * Arrange for iter to be something that _dbus_message_iter_check() would
+ * reject as not a valid iterator.
+ */
+static void
+_dbus_message_real_iter_zero (DBusMessageRealIter *iter)
+{
+ _dbus_assert (iter != NULL);
+ _DBUS_ZERO (*iter);
+ /* NULL is not, strictly speaking, guaranteed to be all-bits-zero */
+ iter->message = NULL;
+}
+
+/**
+ * Initialize iter as if with #DBUS_MESSAGE_ITER_INIT_CLOSED. The only valid
+ * operation for such an iterator is
+ * dbus_message_iter_abandon_container_if_open(), which does nothing.
+ */
+void
+dbus_message_iter_init_closed (DBusMessageIter *iter)
+{
+ _dbus_return_if_fail (iter != NULL);
+ _dbus_message_real_iter_zero ((DBusMessageRealIter *) iter);
+}
+
+static dbus_bool_t
+_dbus_message_real_iter_is_zeroed (DBusMessageRealIter *iter)
+{
+ return (iter != NULL && iter->message == NULL && iter->changed_stamp == 0 &&
+ iter->iter_type == 0 && iter->sig_refcount == 0);
+}
+
+#if defined(DBUS_ENABLE_CHECKS) || defined(DBUS_ENABLE_ASSERT)
+static dbus_bool_t
+_dbus_message_iter_check (DBusMessageRealIter *iter)
+{
+ char byte_order;
+
+ if (iter == NULL)
+ {
+ _dbus_warn_check_failed ("dbus message iterator is NULL");
+ return FALSE;
+ }
+
+ if (iter->message == NULL || iter->iter_type == 0)
+ {
+ _dbus_warn_check_failed ("dbus message iterator has already been "
+ "closed, or is uninitialized or corrupt");
+ return FALSE;
+ }
+
+ byte_order = _dbus_header_get_byte_order (&iter->message->header);
+
+ if (iter->iter_type == DBUS_MESSAGE_ITER_TYPE_READER)
+ {
+ if (iter->u.reader.byte_order != byte_order)
+ {
+ _dbus_warn_check_failed ("dbus message changed byte order since iterator was created");
+ 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 != byte_order)
+ {
+ _dbus_warn_check_failed ("dbus message changed byte order since append iterator was created");
+ 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");
+ 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)");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+#endif /* DBUS_ENABLE_CHECKS || DBUS_ENABLE_ASSERT */
+
+/**
+ * Implementation of the varargs arg-getting functions.
+ * dbus_message_get_args() is the place to go for complete
+ * documentation.
+ *
+ * @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, j;
+ dbus_bool_t retval;
+ va_list copy_args;
+
+ _dbus_assert (_dbus_message_iter_check (real));
+
+ retval = FALSE;
+
+ spec_type = first_arg_type;
+ i = 0;
+
+ /* copy var_args first, then we can do another iteration over it to
+ * free memory and close unix fds if parse failed at some point.
+ */
+ va_copy (copy_args, var_args);
+
+ 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))
+ {
+ void *ptr;
+
+ ptr = va_arg (var_args, void *);
+
+ _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 void **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 void **);
+ 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, ptr, n_elements_p);
+ }
+ else if (_DBUS_TYPE_IS_STRINGLIKE (spec_element_type))
+ {
+ 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);
+
+ j = 0;
+ while (j < n_elements)
+ {
+ const char *s;
+ _dbus_type_reader_read_basic (&array,
+ (void *) &s);
+
+ str_array[j] = _dbus_strdup (s);
+ if (str_array[j] == NULL)
+ {
+ dbus_free_string_array (str_array);
+ _DBUS_SET_OOM (error);
+ goto out;
+ }
+
+ ++j;
+
+ if (!_dbus_type_reader_next (&array))
+ _dbus_assert (j == n_elements);
+ }
+
+ _dbus_assert (_dbus_type_reader_get_current_type (&array) == DBUS_TYPE_INVALID);
+ _dbus_assert (j == n_elements);
+ _dbus_assert (str_array[j] == 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",
+ _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",
+ _DBUS_FUNCTION_NAME);
+ goto out;
+ }
+#endif
+
+ /* how many arguments already handled */
+ i++;
+
+ 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;
+ }
+ }
+
+ retval = TRUE;
+
+ out:
+ /* there may memory or unix fd leak in the above iteration if parse failed.
+ * so we have another iteration over copy_args to free memory and close
+ * unix fds.
+ */
+ if (!retval)
+ {
+ spec_type = first_arg_type;
+ j = 0;
+
+ while (j < i)
+ {
+ if (spec_type == DBUS_TYPE_UNIX_FD)
+ {
+#ifdef HAVE_UNIX_FD_PASSING
+ int *pfd;
+
+ pfd = va_arg (copy_args, int *);
+ _dbus_assert(pfd);
+ if (*pfd >= 0)
+ {
+ _dbus_close (*pfd, NULL);
+ *pfd = -1;
+ }
+#endif
+ }
+ else if (dbus_type_is_basic (spec_type))
+ {
+ /* move the index forward */
+ va_arg (copy_args, const void *);
+ }
+ else if (spec_type == DBUS_TYPE_ARRAY)
+ {
+ int spec_element_type;
+
+ spec_element_type = va_arg (copy_args, int);
+ if (dbus_type_is_fixed (spec_element_type))
+ {
+ /* move the index forward */
+ va_arg (copy_args, const void **);
+ va_arg (copy_args, int *);
+ }
+ else if (_DBUS_TYPE_IS_STRINGLIKE (spec_element_type))
+ {
+ char ***str_array_p;
+
+ str_array_p = va_arg (copy_args, char ***);
+ /* move the index forward */
+ va_arg (copy_args, int *);
+ _dbus_assert (str_array_p != NULL);
+ dbus_free_string_array (*str_array_p);
+ *str_array_p = NULL;
+ }
+ }
+
+ spec_type = va_arg (copy_args, int);
+ j++;
+ }
+ }
+
+ va_end (copy_args);
+ 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)
+{
+ DBusBasicValue value;
+
+ _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 */
+
+ value.u32 = reply_serial;
+
+ return _dbus_header_set_field_basic (&message->header,
+ DBUS_HEADER_FIELD_REPLY_SERIAL,
+ DBUS_TYPE_UINT32,
+ &value);
+}
+
+/**
+ * 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 (_dbus_atomic_get (&message->refcount) == 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 (_dbus_atomic_get (&message->refcount) == 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_new0 (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
+ }
+
+ _dbus_atomic_inc (&message->refcount);
+
+ _dbus_message_trace_ref (message, 0, 1, "new_empty_header");
+
+ 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);
+ _dbus_string_set_length (&message->body, 0);
+ }
+ else
+ {
+ if (!_dbus_header_init (&message->header))
+ {
+ 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,
+ DBUS_COMPILER_BYTE_ORDER,
+ 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 iface 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 *iface,
+ const char *method)
+{
+ DBusMessage *message;
+
+ _dbus_return_val_if_fail (path != NULL, NULL);
+ _dbus_return_val_if_fail (method != NULL, NULL);
+ _dbus_return_val_if_fail (destination == NULL ||
+ _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 (iface == NULL ||
+ _dbus_check_is_valid_interface (iface), NULL);
+ _dbus_return_val_if_fail (_dbus_check_is_valid_member (method), NULL);
+
+ message = dbus_message_new_empty_header ();
+ if (message == NULL)
+ return NULL;
+
+ if (!_dbus_header_create (&message->header,
+ DBUS_COMPILER_BYTE_ORDER,
+ DBUS_MESSAGE_TYPE_METHOD_CALL,
+ destination, path, iface, method, NULL))
+ {
+ dbus_message_unref (message);
+ return NULL;
+ }
+
+ return message;
+}
+
+/**
+ * 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_COMPILER_BYTE_ORDER,
+ DBUS_MESSAGE_TYPE_METHOD_RETURN,
+ sender, NULL, NULL, NULL, NULL))
+ {
+ dbus_message_unref (message);
+ return NULL;
+ }
+
+ dbus_message_set_no_reply (message, TRUE);
+
+ if (!dbus_message_set_reply_serial (message,
+ 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 iface 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 *iface,
+ const char *name)
+{
+ DBusMessage *message;
+
+ _dbus_return_val_if_fail (path != NULL, NULL);
+ _dbus_return_val_if_fail (iface != 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 (iface), 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_COMPILER_BYTE_ORDER,
+ DBUS_MESSAGE_TYPE_SIGNAL,
+ NULL, path, iface, name, NULL))
+ {
+ dbus_message_unref (message);
+ return NULL;
+ }
+
+ dbus_message_set_no_reply (message, TRUE);
+
+ return message;
+}
+
+/**
+ * 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_COMPILER_BYTE_ORDER,
+ DBUS_MESSAGE_TYPE_ERROR,
+ sender, NULL, NULL, NULL, error_name))
+ {
+ dbus_message_unref (message);
+ return NULL;
+ }
+
+ dbus_message_set_no_reply (message, TRUE);
+
+ if (!dbus_message_set_reply_serial (message,
+ 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;
+
+ _dbus_atomic_inc (&retval->refcount);
+
+ 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
+
+ _dbus_message_trace_ref (retval, 0, 1, "copy");
+ 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);
+ _dbus_message_trace_ref (message, old_refcount, old_refcount + 1, "ref");
+
+ 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 >= 1);
+
+ _dbus_message_trace_ref (message, old_refcount, old_refcount - 1, "unref");
+
+ 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 void *value;
+ value = va_arg (var_args, const void *);
+
+ 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 void **value;
+ int n_elements;
+
+ value = va_arg (var_args, const void **);
+ 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 (_DBUS_TYPE_IS_STRINGLIKE (element_type))
+ {
+ 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",
+ _dbus_type_to_string (element_type),
+ _DBUS_FUNCTION_NAME);
+ dbus_message_iter_abandon_container (&iter, &array);
+ 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",
+ _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)
+{
+ /* If these static assertions fail on your platform, report it as a bug. */
+ _DBUS_STATIC_ASSERT (sizeof (DBusMessageRealIter) <= sizeof (DBusMessageIter));
+ _DBUS_STATIC_ASSERT (_DBUS_ALIGNOF (DBusMessageRealIter) <=
+ _DBUS_ALIGNOF (DBusMessageIter));
+#if CHECK_DBUS_1_10_BINARY_COMPATIBILITY
+ /* A failure of these two assertions would indicate that we've broken
+ * ABI on this platform since 1.10.0. */
+ _DBUS_STATIC_ASSERT (sizeof (DBusMessageIter_1_10_0) ==
+ sizeof (DBusMessageIter));
+ _DBUS_STATIC_ASSERT (_DBUS_ALIGNOF (DBusMessageIter_1_10_0) ==
+ _DBUS_ALIGNOF (DBusMessageIter));
+#endif
+ /* If this static assertion fails, it means the DBusMessageIter struct
+ * is not "packed", which might result in "iter = other_iter" not copying
+ * every byte. */
+#if DBUS_SIZEOF_VOID_P > 8
+ _DBUS_STATIC_ASSERT (sizeof (DBusMessageIter) == 16 * sizeof (void *));
+#else
+ _DBUS_STATIC_ASSERT (sizeof (DBusMessageIter) ==
+ 4 * sizeof (void *) + sizeof (dbus_uint32_t) + 9 * sizeof (int));
+#endif
+
+ /* 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,
+ _dbus_header_get_byte_order (&message->header),
+ 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 = NULL;
+ 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))
+ goto oom;
+
+ /* This is correct whether it succeeds or fails: on success it sets `ret`,
+ * and on failure it leaves `ret` set to NULL. */
+ _dbus_string_steal_data (&retstr, &ret);
+
+oom:
+ _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 a #DBusBasicValue,
+ * so in versions of libdbus that have that type, you can write code like this:
+ *
+ * @code
+ * DBusBasicValue 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
+ *
+ * (All D-Bus basic types are either numeric and 8 bytes or smaller, or
+ * behave like a string; so in older versions of libdbus, DBusBasicValue
+ * can be replaced with union { char *string; unsigned char bytes[8]; },
+ * for instance.)
+ *
+ * @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 elements in the array-typed value pointed
+ * to by the iterator.
+ * Note that this function is O(1) for arrays of fixed-size types
+ * but O(n) for arrays of variable-length types such as strings,
+ * so it may be a bad idea to use it.
+ *
+ * @param iter the iterator
+ * @returns the number of elements in the array
+ */
+int
+dbus_message_iter_get_element_count (DBusMessageIter *iter)
+{
+ DBusMessageRealIter *real = (DBusMessageRealIter *)iter;
+ DBusTypeReader array;
+ int element_type;
+ int n_elements = 0;
+
+ _dbus_return_val_if_fail (_dbus_message_iter_check (real), 0);
+ _dbus_return_val_if_fail (_dbus_type_reader_get_current_type (&real->u.reader)
+ == DBUS_TYPE_ARRAY, 0);
+
+ element_type = _dbus_type_reader_get_element_type (&real->u.reader);
+ _dbus_type_reader_recurse (&real->u.reader, &array);
+ if (dbus_type_is_fixed (element_type))
+ {
+ int alignment = _dbus_type_get_alignment (element_type);
+ int total_len = _dbus_type_reader_get_array_length (&array);
+ n_elements = total_len / alignment;
+ }
+ else
+ {
+ while (_dbus_type_reader_get_current_type (&array) != DBUS_TYPE_INVALID)
+ {
+ ++n_elements;
+ _dbus_type_reader_next (&array);
+ }
+ }
+
+ return n_elements;
+}
+
+/**
+ * Returns the number of bytes in the array as marshaled in the wire
+ * protocol. The iterator must currently be inside an array-typed
+ * value.
+ *
+ * This function is deprecated on the grounds that it is stupid. Why
+ * would you want to know how many bytes are in the array as marshaled
+ * in the wire protocol? Use dbus_message_iter_get_element_count() instead.
+ *
+ * @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;
+#ifndef DBUS_DISABLE_CHECKS
+ 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));
+#endif
+
+ _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,
+ _dbus_header_get_byte_order (&message->header),
+ &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,
+ &current_sig, &current_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;
+
+ /* If this assertion failed, then str would be neither stored in u.writer
+ * nor freed by this function, resulting in a memory leak. */
+ _dbus_assert (real->u.writer.type_str == NULL);
+ _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);
+}
+
+#if defined(DBUS_ENABLE_CHECKS) || defined(DBUS_ENABLE_ASSERT)
+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)");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+#endif
+
+#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)
+ {
+ DBusString str;
+ DBusValidity signature_validity;
+ 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_string_init_const (&str, *string_p);
+ signature_validity = _dbus_validate_signature_with_reason (&str,
+ 0,
+ _dbus_string_get_length (&str));
+
+ if (signature_validity == DBUS_VALIDITY_UNKNOWN_OOM_ERROR)
+ return FALSE;
+
+ _dbus_return_val_if_fail (signature_validity == DBUS_VALID, 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;
+
+ ret = FALSE;
+
+ /* First step, include the fd in the fd list of this message */
+ if (!(fds = expand_fd_array(real->message, 1)))
+ goto out;
+
+ *fds = _dbus_dup(*(int*) value, NULL);
+ if (*fds < 0)
+ goto out;
+
+ 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);
+ goto out;
+ }
+
+ 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;
+ /* This is redundant (we could just fall through), but it avoids
+ * -Wunused-label in builds that don't HAVE_UNIX_FD_PASSING */
+ goto out;
+#endif
+ }
+ else
+ {
+ ret = _dbus_type_writer_write_basic (&real->u.writer, type, value);
+ }
+
+out:
+ 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. On success, 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.
+ *
+ * If this function fails, the sub-iterator remains invalid, and must
+ * not be closed with dbus_message_iter_close_container() or abandoned
+ * with dbus_message_iter_abandon_container(). However, after this
+ * function has either succeeded or failed, it is valid to call
+ * dbus_message_iter_abandon_container_if_open().
+ *
+ * @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;
+ DBusValidity contained_signature_validity;
+ dbus_bool_t ret;
+
+ _dbus_return_val_if_fail (sub != NULL, FALSE);
+ /* Do our best to make sure the sub-iterator doesn't contain something
+ * valid-looking on failure */
+ _dbus_message_real_iter_zero (real_sub);
+
+ _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 ((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)
+ */
+ if (contained_signature != NULL)
+ {
+ _dbus_string_init_const (&contained_str, contained_signature);
+ contained_signature_validity = _dbus_validate_signature_with_reason (&contained_str,
+ 0,
+ _dbus_string_get_length (&contained_str));
+
+ if (contained_signature_validity == DBUS_VALIDITY_UNKNOWN_OOM_ERROR)
+ return FALSE;
+ }
+ else
+ {
+ /* just some placeholder value */
+ contained_signature_validity = DBUS_VALID_BUT_INCOMPLETE;
+ }
+
+ _dbus_return_val_if_fail ((type == DBUS_TYPE_ARRAY && contained_signature && *contained_signature == DBUS_DICT_ENTRY_BEGIN_CHAR) ||
+ contained_signature == NULL ||
+ contained_signature_validity == DBUS_VALID,
+ FALSE);
+
+ if (!_dbus_message_iter_open_signature (real))
+ return FALSE;
+
+ ret = FALSE;
+ *real_sub = *real;
+
+ if (contained_signature != NULL)
+ {
+ _dbus_string_init_const (&contained_str, contained_signature);
+
+ ret = _dbus_type_writer_recurse (&real->u.writer,
+ type,
+ &contained_str, 0,
+ &real_sub->u.writer);
+ }
+ else
+ {
+ ret = _dbus_type_writer_recurse (&real->u.writer,
+ type,
+ NULL, 0,
+ &real_sub->u.writer);
+ }
+
+ if (!ret)
+ _dbus_message_iter_abandon_signature (real);
+
+ return ret;
+}
+
+
+/**
+ * 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().
+ *
+ * Even if this function fails due to lack of memory, the sub-iterator sub
+ * has been closed and invalidated. It must not be closed again with this
+ * function, or abandoned with dbus_message_iter_abandon_container().
+ * However, it remains valid to call
+ * dbus_message_iter_abandon_container_if_open().
+ *
+ * @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);
+ _dbus_message_real_iter_zero (real_sub);
+
+ 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;
+
+#ifndef DBUS_DISABLE_CHECKS
+ _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);
+#endif
+
+ _dbus_message_iter_abandon_signature (real);
+ _dbus_message_real_iter_zero (real_sub);
+}
+
+/**
+ * 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.
+ *
+ * Unlike dbus_message_iter_abandon_container(), it is valid to call this
+ * function on an iterator that was initialized with
+ * #DBUS_MESSAGE_ITER_INIT_CLOSED, or an iterator that was already closed
+ * or abandoned. However, it is not valid to call this function on
+ * uninitialized memory. This is intended to be used in error cleanup
+ * code paths, similar to this pattern:
+ *
+ * DBusMessageIter outer = DBUS_MESSAGE_ITER_INIT_CLOSED;
+ * DBusMessageIter inner = DBUS_MESSAGE_ITER_INIT_CLOSED;
+ * dbus_bool_t result = FALSE;
+ *
+ * if (!dbus_message_iter_open_container (iter, ..., &outer))
+ * goto out;
+ *
+ * if (!dbus_message_iter_open_container (&outer, ..., &inner))
+ * goto out;
+ *
+ * if (!dbus_message_iter_append_basic (&inner, ...))
+ * goto out;
+ *
+ * if (!dbus_message_iter_close_container (&outer, ..., &inner))
+ * goto out;
+ *
+ * if (!dbus_message_iter_close_container (iter, ..., &outer))
+ * goto out;
+ *
+ * result = TRUE;
+ *
+ * out:
+ * dbus_message_iter_abandon_container_if_open (&outer, &inner);
+ * dbus_message_iter_abandon_container_if_open (iter, &outer);
+ * return result;
+ *
+ * @param iter the append iterator
+ * @param sub sub-iterator to close
+ */
+void
+dbus_message_iter_abandon_container_if_open (DBusMessageIter *iter,
+ DBusMessageIter *sub)
+{
+ DBusMessageRealIter *real = (DBusMessageRealIter *)iter;
+ DBusMessageRealIter *real_sub = (DBusMessageRealIter *)sub;
+
+ /* If both the parent and the child are zeroed out, then either we didn't
+ * even get as far as successfully recursing into the parent, or we already
+ * closed both the child and the parent. For example, in the code sample
+ * in the doc-comment above, this happens for
+ * abandon_container_if_open (&outer, &inner) if the first open_container
+ * call failed, or if we reached result = TRUE and fell through. */
+ if (_dbus_message_real_iter_is_zeroed (real) &&
+ _dbus_message_real_iter_is_zeroed (real_sub))
+ return;
+
+#ifndef DBUS_DISABLE_CHECKS
+ /* If the child is not zeroed out, but the parent is, then something has
+ * gone horribly wrong (in practice that would probably mean both are
+ * uninitialized or corrupt, and the parent happens to have ended up
+ * all-bytes-zero). */
+ _dbus_return_if_fail (_dbus_message_iter_append_check (real));
+ _dbus_return_if_fail (real->iter_type == DBUS_MESSAGE_ITER_TYPE_WRITER);
+#endif
+
+ /* If the parent is not zeroed out, but the child is, then either we did
+ * not successfully open the child, or we already closed the child. This
+ * means we do not own a reference to the parent's signature, so it would
+ * be wrong to release it; so we must not call abandon_signature() here.
+ * In the code sample in the doc-comment above, this happens for
+ * abandon_container_if_open (&outer, &inner) if the second open_container
+ * call failed, or if the second close_container call failed. */
+ if (_dbus_message_real_iter_is_zeroed (real_sub))
+ return;
+
+#ifndef DBUS_DISABLE_CHECKS
+ _dbus_return_if_fail (_dbus_message_iter_append_check (real_sub));
+ _dbus_return_if_fail (real_sub->iter_type == DBUS_MESSAGE_ITER_TYPE_WRITER);
+#endif
+
+ /* If neither the parent nor the child is zeroed out, then we genuinely
+ * have an open container; close it. In the code sample in the doc-comment,
+ * this happens for abandon_container_if_open (&outer, &inner) if the
+ * append_basic call failed. */
+ _dbus_message_iter_abandon_signature (real);
+ _dbus_message_real_iter_zero (real_sub);
+}
+
+/**
+ * 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 iface the interface or #NULL to unset
+ * @returns #FALSE if not enough memory
+ */
+dbus_bool_t
+dbus_message_set_interface (DBusMessage *message,
+ const char *iface)
+{
+ _dbus_return_val_if_fail (message != NULL, FALSE);
+ _dbus_return_val_if_fail (!message->locked, FALSE);
+ _dbus_return_val_if_fail (iface == NULL ||
+ _dbus_check_is_valid_interface (iface),
+ FALSE);
+
+ return set_or_delete_string_field (message,
+ DBUS_HEADER_FIELD_INTERFACE,
+ DBUS_TYPE_STRING,
+ iface);
+}
+
+/**
+ * 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 iface the interface name
+ * @returns #TRUE if the interface field in the header matches
+ */
+dbus_bool_t
+dbus_message_has_interface (DBusMessage *message,
+ const char *iface)
+{
+ const char *msg_interface;
+ msg_interface = dbus_message_get_interface (message);
+
+ if (msg_interface == NULL)
+ {
+ if (iface == NULL)
+ return TRUE;
+ else
+ return FALSE;
+ }
+
+ if (iface == NULL)
+ return FALSE;
+
+ if (strcmp (msg_interface, iface) == 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 *iface,
+ const char *member)
+{
+ const char *n;
+
+ _dbus_assert (message != NULL);
+ _dbus_assert (iface != 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, iface) == 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 iface 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 *iface,
+ const char *method)
+{
+ _dbus_return_val_if_fail (message != NULL, FALSE);
+ _dbus_return_val_if_fail (iface != 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,
+ iface, 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 iface 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 *iface,
+ const char *signal_name)
+{
+ _dbus_return_val_if_fail (message != NULL, FALSE);
+ _dbus_return_val_if_fail (iface != 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,
+ iface, 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
+ */
+dbus_bool_t
+dbus_message_has_destination (DBusMessage *message,
+ const char *name)
+{
+ const char *s;
+
+ _dbus_return_val_if_fail (message != NULL, FALSE);
+ _dbus_return_val_if_fail (name != NULL, FALSE);
+ /* don't check that name is valid since it would be expensive, and
+ * not catch many common errors
+ */
+
+ s = dbus_message_get_destination (message);
+
+ if (s && strcmp (s, name) == 0)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+/**
+ * Checks whether the message has the given unique name as its sender.
+ * If the message has no sender specified or has a different sender,
+ * returns #FALSE. Note that a peer application will always have the
+ * unique name of the connection as the sender. So you can't use this
+ * function to see whether a sender owned a well-known name.
+ *
+ * Messages from the bus itself will have #DBUS_SERVICE_DBUS
+ * as the sender.
+ *
+ * @param message the message
+ * @param name the name to check (must not be #NULL)
+ *
+ * @returns #TRUE if the message has the given sender
+ */
+dbus_bool_t
+dbus_message_has_sender (DBusMessage *message,
+ const char *name)
+{
+ const char *s;
+
+ _dbus_return_val_if_fail (message != NULL, FALSE);
+ _dbus_return_val_if_fail (name != NULL, FALSE);
+ /* don't check that name is valid since it would be expensive, and
+ * not catch many common errors
+ */
+
+ s = dbus_message_get_sender (message);
+
+ if (s && strcmp (s, name) == 0)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+/**
+ * Checks whether the message has the given signature; see
+ * dbus_message_get_signature() for more details on what the signature
+ * looks like.
+ *
+ * @param message the message
+ * @param signature typecode array
+ * @returns #TRUE if message has the given signature
+*/
+dbus_bool_t
+dbus_message_has_signature (DBusMessage *message,
+ const char *signature)
+{
+ const char *s;
+
+ _dbus_return_val_if_fail (message != NULL, FALSE);
+ _dbus_return_val_if_fail (signature != NULL, FALSE);
+ /* don't check that signature is valid since it would be expensive,
+ * and not catch many common errors
+ */
+
+ s = dbus_message_get_signature (message);
+
+ if (s && strcmp (s, signature) == 0)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+/**
+ * Sets a #DBusError based on the contents of the given
+ * message. The error is only set if the message
+ * is an error message, as in #DBUS_MESSAGE_TYPE_ERROR.
+ * The name of the error is set to the name of the message,
+ * and the error message is set to the first argument
+ * if the argument exists and is a string.
+ *
+ * The return value indicates whether the error was set (the error is
+ * set if and only if the message is an error message). So you can
+ * check for an error reply and convert it to DBusError in one go:
+ * @code
+ * if (dbus_set_error_from_message (error, reply))
+ * return error;
+ * else
+ * process reply;
+ * @endcode
+ *
+ * @param error the error to set
+ * @param message the message to set it from
+ * @returns #TRUE if the message had type #DBUS_MESSAGE_TYPE_ERROR
+ */
+dbus_bool_t
+dbus_set_error_from_message (DBusError *error,
+ DBusMessage *message)
+{
+ const char *str;
+
+ _dbus_return_val_if_fail (message != NULL, FALSE);
+ _dbus_return_val_if_error_is_set (error, FALSE);
+
+ if (dbus_message_get_type (message) != DBUS_MESSAGE_TYPE_ERROR)
+ return FALSE;
+
+ str = NULL;
+ dbus_message_get_args (message, NULL,
+ DBUS_TYPE_STRING, &str,
+ DBUS_TYPE_INVALID);
+
+ dbus_set_error (error, dbus_message_get_error_name (message),
+ str ? "%s" : NULL, str);
+
+ return TRUE;
+}
+
+/**
+ * Checks whether a message contains unix fds
+ *
+ * @param message the message
+ * @returns #TRUE if the message contains unix fds
+ */
+dbus_bool_t
+dbus_message_contains_unix_fds(DBusMessage *message)
+{
+#ifdef HAVE_UNIX_FD_PASSING
+ _dbus_assert(message);
+
+ return message->n_unix_fds > 0;
+#else
+ return FALSE;
+#endif
+}
+
+/**
+ * Sets the container instance this message was sent from.
+ *
+ * The path must contain only valid characters for an object path
+ * 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_container_instance (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_CONTAINER_INSTANCE,
+ DBUS_TYPE_OBJECT_PATH,
+ object_path);
+}
+
+/**
+ * Gets the container instance this message was sent from, 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 path (should not be freed) or #NULL
+ */
+const char *
+dbus_message_get_container_instance (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_CONTAINER_INSTANCE,
+ DBUS_TYPE_OBJECT_PATH,
+ (void *) &v);
+ return v;
+}
+
+/** @} */
+
+/**
+ * @addtogroup DBusMessageInternals
+ *
+ * @{
+ */
+
+/**
+ * The initial buffer size of the message loader.
+ *
+ * @todo this should be based on min header size plus some average
+ * body size, or something. Or rather, the min header size only, if we
+ * want to try to read only the header, store that in a DBusMessage,
+ * then read only the body and store that, etc., depends on
+ * how we optimize _dbus_message_loader_get_buffer() and what
+ * the exact message format is.
+ */
+#define INITIAL_LOADER_DATA_LEN 32
+
+/**
+ * Creates a new message loader. Returns #NULL if memory can't
+ * be allocated.
+ *
+ * @returns new loader, or #NULL.
+ */
+DBusMessageLoader*
+_dbus_message_loader_new (void)
+{
+ DBusMessageLoader *loader;
+
+ loader = dbus_new0 (DBusMessageLoader, 1);
+ if (loader == NULL)
+ return NULL;
+
+ loader->refcount = 1;
+
+ loader->corrupted = FALSE;
+ loader->corruption_reason = DBUS_VALID;
+
+ /* this can be configured by the app, but defaults to the protocol max */
+ loader->max_message_size = DBUS_MAXIMUM_MESSAGE_LENGTH;
+
+ /* We set a very relatively conservative default here since due to how
+ SCM_RIGHTS works we need to preallocate an fd array of the maximum
+ number of unix fds we want to receive in advance. A
+ try-and-reallocate loop is not possible. */
+ loader->max_message_unix_fds = DBUS_DEFAULT_MESSAGE_UNIX_FDS;
+
+ if (!_dbus_string_init (&loader->data))
+ {
+ dbus_free (loader);
+ return NULL;
+ }
+
+ /* preallocate the buffer for speed, ignore failure */
+ _dbus_string_set_length (&loader->data, INITIAL_LOADER_DATA_LEN);
+ _dbus_string_set_length (&loader->data, 0);
+
+#ifdef HAVE_UNIX_FD_PASSING
+ loader->unix_fds = NULL;
+ loader->n_unix_fds = loader->n_unix_fds_allocated = 0;
+ loader->unix_fds_outstanding = FALSE;
+#endif
+
+ return loader;
+}
+
+/**
+ * Increments the reference count of the loader.
+ *
+ * @param loader the loader.
+ * @returns the loader
+ */
+DBusMessageLoader *
+_dbus_message_loader_ref (DBusMessageLoader *loader)
+{
+ loader->refcount += 1;
+
+ return loader;
+}
+
+/**
+ * Decrements the reference count of the loader and finalizes the
+ * loader when the count reaches zero.
+ *
+ * @param loader the loader.
+ */
+void
+_dbus_message_loader_unref (DBusMessageLoader *loader)
+{
+ loader->refcount -= 1;
+ if (loader->refcount == 0)
+ {
+#ifdef HAVE_UNIX_FD_PASSING
+ close_unix_fds(loader->unix_fds, &loader->n_unix_fds);
+ dbus_free(loader->unix_fds);
+#endif
+ _dbus_list_clear_full (&loader->messages,
+ (DBusFreeFunction) dbus_message_unref);
+ _dbus_string_free (&loader->data);
+ dbus_free (loader);
+ }
+}
+
+/**
+ * Gets the buffer to use for reading data from the network. Network
+ * data is read directly into an allocated buffer, which is then used
+ * in the DBusMessage, to avoid as many extra memcpy's as possible.
+ * The buffer must always be returned immediately using
+ * _dbus_message_loader_return_buffer(), even if no bytes are
+ * successfully read.
+ *
+ * @todo this function can be a lot more clever. For example
+ * it can probably always return a buffer size to read exactly
+ * the body of the next message, thus avoiding any memory wastage
+ * or reallocs.
+ *
+ * @todo we need to enforce a max length on strings in header fields.
+ *
+ * @param loader the message loader.
+ * @param buffer the buffer
+ */
+void
+_dbus_message_loader_get_buffer (DBusMessageLoader *loader,
+ DBusString **buffer,
+ int *max_to_read,
+ dbus_bool_t *may_read_fds)
+{
+ _dbus_assert (!loader->buffer_outstanding);
+
+ *buffer = &loader->data;
+
+ loader->buffer_outstanding = TRUE;
+
+ if (max_to_read != NULL)
+ {
+#ifdef HAVE_UNIX_FD_PASSING
+ int offset = 0;
+ int remain;
+ int byte_order;
+ int fields_array_len;
+ int header_len;
+ int body_len;
+#endif
+
+ *max_to_read = DBUS_MAXIMUM_MESSAGE_LENGTH;
+ *may_read_fds = TRUE;
+
+#ifdef HAVE_UNIX_FD_PASSING
+ /* If we aren't holding onto any fds, we can read as much as we want
+ * (fast path). */
+ if (loader->n_unix_fds == 0)
+ return;
+
+ /* Slow path: we have a message with some fds in it. We don't want
+ * to start on the next message until this one is out of the way;
+ * otherwise a legitimate sender can keep us processing messages
+ * containing fds, until we disconnect it for having had fds pending
+ * for too long, a limit that is in place to stop malicious senders
+ * from setting up recursive fd-passing that takes up our quota and
+ * will never go away. */
+
+ remain = _dbus_string_get_length (&loader->data);
+
+ while (remain > 0)
+ {
+ DBusValidity validity = DBUS_VALIDITY_UNKNOWN;
+ int needed;
+
+ /* If 0 < remain < DBUS_MINIMUM_HEADER_SIZE, then we've had at
+ * least the first byte of a message, but we don't know how
+ * much more to read. Only read the rest of the
+ * DBUS_MINIMUM_HEADER_SIZE for now; then we'll know. */
+ if (remain < DBUS_MINIMUM_HEADER_SIZE)
+ {
+ *max_to_read = DBUS_MINIMUM_HEADER_SIZE - remain;
+ *may_read_fds = FALSE;
+ return;
+ }
+
+ if (!_dbus_header_have_message_untrusted (loader->max_message_size,
+ &validity,
+ &byte_order,
+ &fields_array_len,
+ &header_len,
+ &body_len,
+ &loader->data,
+ offset,
+ remain))
+ {
+ /* If a message in the buffer is invalid, we're going to
+ * disconnect the sender anyway, so reading an arbitrary amount
+ * is fine. */
+ if (validity != DBUS_VALID)
+ return;
+
+ /* We have a partial message, with the
+ * DBUS_MINIMUM_HEADER_SIZE-byte fixed part of the header (which
+ * lets us work out how much more we need), but no more. Read
+ * the rest of the message. */
+ needed = header_len + body_len;
+ _dbus_assert (needed > remain);
+ *max_to_read = needed - remain;
+ *may_read_fds = FALSE;
+ return;
+ }
+
+ /* Skip over entire messages until we have less than a message
+ * remaining. */
+ needed = header_len + body_len;
+ _dbus_assert (needed > DBUS_MINIMUM_HEADER_SIZE);
+ _dbus_assert (remain >= needed);
+ remain -= needed;
+ offset += needed;
+ }
+#endif
+ }
+}
+
+/**
+ * Returns a buffer obtained from _dbus_message_loader_get_buffer(),
+ * indicating to the loader how many bytes of the buffer were filled
+ * in. This function must always be called, even if no bytes were
+ * successfully read.
+ *
+ * @param loader the loader.
+ * @param buffer the buffer.
+ */
+void
+_dbus_message_loader_return_buffer (DBusMessageLoader *loader,
+ DBusString *buffer)
+{
+ _dbus_assert (loader->buffer_outstanding);
+ _dbus_assert (buffer == &loader->data);
+
+ loader->buffer_outstanding = FALSE;
+}
+
+#ifdef HAVE_UNIX_FD_PASSING
+/**
+ * Gets the buffer to use for reading unix fds from the network.
+ *
+ * This works similar to _dbus_message_loader_get_buffer()
+ *
+ * @param loader the message loader.
+ * @param fds the array to read fds into
+ * @param max_n_fds how many fds to read at most
+ * @return TRUE on success, FALSE on OOM
+ */
+dbus_bool_t
+_dbus_message_loader_get_unix_fds(DBusMessageLoader *loader,
+ int **fds,
+ unsigned *max_n_fds)
+{
+ _dbus_assert (!loader->unix_fds_outstanding);
+
+ /* Allocate space where we can put the fds we read. We allocate
+ space for max_message_unix_fds since this is an
+ upper limit how many fds can be received within a single
+ message. Since SCM_RIGHTS doesn't allow a reallocate+retry logic
+ we are allocating the maximum possible array size right from the
+ beginning. This sucks a bit, however unless SCM_RIGHTS is fixed
+ there is no better way. */
+
+ if (loader->n_unix_fds_allocated < loader->max_message_unix_fds)
+ {
+ int *a = dbus_realloc(loader->unix_fds,
+ loader->max_message_unix_fds * sizeof(loader->unix_fds[0]));
+
+ if (!a)
+ return FALSE;
+
+ loader->unix_fds = a;
+ loader->n_unix_fds_allocated = loader->max_message_unix_fds;
+ }
+
+ *fds = loader->unix_fds + loader->n_unix_fds;
+ *max_n_fds = loader->n_unix_fds_allocated - loader->n_unix_fds;
+
+ loader->unix_fds_outstanding = TRUE;
+ return TRUE;
+}
+
+/**
+ * Returns a buffer obtained from _dbus_message_loader_get_unix_fds().
+ *
+ * This works similar to _dbus_message_loader_return_buffer()
+ *
+ * @param loader the message loader.
+ * @param fds the array fds were read into
+ * @param n_fds how many fds were read
+ */
+
+void
+_dbus_message_loader_return_unix_fds(DBusMessageLoader *loader,
+ int *fds,
+ unsigned n_fds)
+{
+ _dbus_assert(loader->unix_fds_outstanding);
+ _dbus_assert(loader->unix_fds + loader->n_unix_fds == fds);
+ _dbus_assert(loader->n_unix_fds + n_fds <= loader->n_unix_fds_allocated);
+
+ loader->n_unix_fds += n_fds;
+ loader->unix_fds_outstanding = FALSE;
+
+ if (n_fds && loader->unix_fds_change)
+ loader->unix_fds_change (loader->unix_fds_change_data);
+}
+#endif
+
+/*
+ * FIXME when we move the header out of the buffer, that memmoves all
+ * buffered messages. Kind of crappy.
+ *
+ * Also we copy the header and body, which is kind of crappy. To
+ * avoid this, we have to allow header and body to be in a single
+ * memory block, which is good for messages we read and bad for
+ * messages we are creating. But we could move_len() the buffer into
+ * this single memory block, and move_len() will just swap the buffers
+ * if you're moving the entire buffer replacing the dest string.
+ *
+ * We could also have the message loader tell the transport how many
+ * bytes to read; so it would first ask for some arbitrary number like
+ * 256, then if the message was incomplete it would use the
+ * header/body len to ask for exactly the size of the message (or
+ * blocks the size of a typical kernel buffer for the socket). That
+ * way we don't get trailing bytes in the buffer that have to be
+ * memmoved. Though I suppose we also don't have a chance of reading a
+ * bunch of small messages at once, so the optimization may be stupid.
+ *
+ * Another approach would be to keep a "start" index into
+ * loader->data and only delete it occasionally, instead of after
+ * each message is loaded.
+ *
+ * load_message() returns FALSE if not enough memory OR the loader was corrupted
+ */
+static dbus_bool_t
+load_message (DBusMessageLoader *loader,
+ DBusMessage *message,
+ int byte_order,
+ int fields_array_len,
+ int header_len,
+ int body_len)
+{
+ dbus_bool_t oom;
+ DBusValidity validity;
+ const DBusString *type_str;
+ int type_pos;
+ DBusValidationMode mode;
+ dbus_uint32_t n_unix_fds = 0;
+
+ mode = DBUS_VALIDATION_MODE_DATA_IS_UNTRUSTED;
+
+ oom = FALSE;
+
+#if 0
+ _dbus_verbose_bytes_of_string (&loader->data, 0, header_len /* + body_len */);
+#endif
+
+ /* 1. VALIDATE AND COPY OVER HEADER */
+ _dbus_assert (_dbus_string_get_length (&message->header.data) == 0);
+ _dbus_assert ((header_len + body_len) <= _dbus_string_get_length (&loader->data));
+
+ if (!_dbus_header_load (&message->header,
+ mode,
+ &validity,
+ byte_order,
+ fields_array_len,
+ header_len,
+ body_len,
+ &loader->data))
+ {
+ _dbus_verbose ("Failed to load header for new message code %d\n", validity);
+
+ /* assert here so we can catch any code that still uses DBUS_VALID to indicate
+ oom errors. They should use DBUS_VALIDITY_UNKNOWN_OOM_ERROR instead */
+ _dbus_assert (validity != DBUS_VALID);
+
+ if (validity == DBUS_VALIDITY_UNKNOWN_OOM_ERROR)
+ oom = TRUE;
+ else
+ {
+ loader->corrupted = TRUE;
+ loader->corruption_reason = validity;
+ }
+ goto failed;
+ }
+
+ _dbus_assert (validity == DBUS_VALID);
+
+ /* 2. VALIDATE BODY */
+ if (mode != DBUS_VALIDATION_MODE_WE_TRUST_THIS_DATA_ABSOLUTELY)
+ {
+ get_const_signature (&message->header, &type_str, &type_pos);
+
+ /* Because the bytes_remaining arg is NULL, this validates that the
+ * body is the right length
+ */
+ validity = _dbus_validate_body_with_reason (type_str,
+ type_pos,
+ byte_order,
+ NULL,
+ &loader->data,
+ header_len,
+ body_len);
+ if (validity != DBUS_VALID)
+ {
+ _dbus_verbose ("Failed to validate message body code %d\n", validity);
+
+ loader->corrupted = TRUE;
+ loader->corruption_reason = validity;
+
+ goto failed;
+ }
+ }
+
+ /* 3. COPY OVER UNIX FDS */
+ _dbus_header_get_field_basic(&message->header,
+ DBUS_HEADER_FIELD_UNIX_FDS,
+ DBUS_TYPE_UINT32,
+ &n_unix_fds);
+
+#ifdef HAVE_UNIX_FD_PASSING
+
+ if (n_unix_fds > loader->n_unix_fds)
+ {
+ _dbus_verbose("Message contains references to more unix fds than were sent %u != %u\n",
+ n_unix_fds, loader->n_unix_fds);
+
+ loader->corrupted = TRUE;
+ loader->corruption_reason = DBUS_INVALID_MISSING_UNIX_FDS;
+ goto failed;
+ }
+
+ /* If this was a recycled message there might still be
+ some memory allocated for the fds */
+ dbus_free(message->unix_fds);
+
+ if (n_unix_fds > 0)
+ {
+ message->unix_fds = _dbus_memdup(loader->unix_fds, n_unix_fds * sizeof(message->unix_fds[0]));
+ if (message->unix_fds == NULL)
+ {
+ _dbus_verbose ("Failed to allocate file descriptor array\n");
+ oom = TRUE;
+ goto failed;
+ }
+
+ message->n_unix_fds_allocated = message->n_unix_fds = n_unix_fds;
+ loader->n_unix_fds -= n_unix_fds;
+ memmove (loader->unix_fds, loader->unix_fds + n_unix_fds, loader->n_unix_fds * sizeof (loader->unix_fds[0]));
+
+ if (loader->unix_fds_change)
+ loader->unix_fds_change (loader->unix_fds_change_data);
+ }
+ else
+ message->unix_fds = NULL;
+
+#else
+
+ if (n_unix_fds > 0)
+ {
+ _dbus_verbose ("Hmm, message claims to come with file descriptors "
+ "but that's not supported on our platform, disconnecting.\n");
+
+ loader->corrupted = TRUE;
+ loader->corruption_reason = DBUS_INVALID_MISSING_UNIX_FDS;
+ goto failed;
+ }
+
+#endif
+
+ /* 3. COPY OVER BODY AND QUEUE MESSAGE */
+
+ if (!_dbus_list_append (&loader->messages, message))
+ {
+ _dbus_verbose ("Failed to append new message to loader queue\n");
+ oom = TRUE;
+ goto failed;
+ }
+
+ _dbus_assert (_dbus_string_get_length (&message->body) == 0);
+ _dbus_assert (_dbus_string_get_length (&loader->data) >=
+ (header_len + body_len));
+
+ if (!_dbus_string_copy_len (&loader->data, header_len, body_len, &message->body, 0))
+ {
+ _dbus_verbose ("Failed to move body into new message\n");
+ oom = TRUE;
+ goto failed;
+ }
+
+ _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);
+
+ _dbus_verbose ("Loaded message %p\n", message);
+
+ _dbus_assert (!oom);
+ _dbus_assert (!loader->corrupted);
+ _dbus_assert (loader->messages != NULL);
+ _dbus_assert (_dbus_list_find_last (&loader->messages, message) != NULL);
+
+ return TRUE;
+
+ failed:
+
+ /* Clean up */
+
+ /* does nothing if the message isn't in the list */
+ _dbus_list_remove_last (&loader->messages, message);
+
+ if (oom)
+ _dbus_assert (!loader->corrupted);
+ else
+ _dbus_assert (loader->corrupted);
+
+ _dbus_verbose_bytes_of_string (&loader->data, 0, _dbus_string_get_length (&loader->data));
+
+ return FALSE;
+}
+
+/**
+ * Converts buffered data into messages, if we have enough data. If
+ * we don't have enough data, does nothing.
+ *
+ * @todo we need to check that the proper named header fields exist
+ * for each message type.
+ *
+ * @todo If a message has unknown type, we should probably eat it
+ * right here rather than passing it out to applications. However
+ * it's not an error to see messages of unknown type.
+ *
+ * @param loader the loader.
+ * @returns #TRUE if we had enough memory to finish.
+ */
+dbus_bool_t
+_dbus_message_loader_queue_messages (DBusMessageLoader *loader)
+{
+ while (!loader->corrupted &&
+ _dbus_string_get_length (&loader->data) >= DBUS_MINIMUM_HEADER_SIZE)
+ {
+ DBusValidity validity;
+ int byte_order, fields_array_len, header_len, body_len;
+
+ if (_dbus_header_have_message_untrusted (loader->max_message_size,
+ &validity,
+ &byte_order,
+ &fields_array_len,
+ &header_len,
+ &body_len,
+ &loader->data, 0,
+ _dbus_string_get_length (&loader->data)))
+ {
+ DBusMessage *message;
+
+ _dbus_assert (validity == DBUS_VALID);
+
+ message = dbus_message_new_empty_header ();
+ if (message == NULL)
+ return FALSE;
+
+ if (!load_message (loader, message,
+ byte_order, fields_array_len,
+ header_len, body_len))
+ {
+ dbus_message_unref (message);
+ /* load_message() returns false if corrupted or OOM; if
+ * corrupted then return TRUE for not OOM
+ */
+ return loader->corrupted;
+ }
+
+ _dbus_assert (loader->messages != NULL);
+ _dbus_assert (_dbus_list_find_last (&loader->messages, message) != NULL);
+ }
+ else
+ {
+ _dbus_verbose ("Initial peek at header says we don't have a whole message yet, or data broken with invalid code %d\n",
+ validity);
+ if (validity != DBUS_VALID)
+ {
+ loader->corrupted = TRUE;
+ loader->corruption_reason = validity;
+ }
+ return TRUE;
+ }
+ }
+
+ return TRUE;
+}
+
+/**
+ * Peeks at first loaded message, returns #NULL if no messages have
+ * been queued.
+ *
+ * @param loader the loader.
+ * @returns the next message, or #NULL if none.
+ */
+DBusMessage*
+_dbus_message_loader_peek_message (DBusMessageLoader *loader)
+{
+ if (loader->messages)
+ return loader->messages->data;
+ else
+ return NULL;
+}
+
+/**
+ * Pops a loaded message (passing ownership of the message
+ * to the caller). Returns #NULL if no messages have been
+ * queued.
+ *
+ * @param loader the loader.
+ * @returns the next message, or #NULL if none.
+ */
+DBusMessage*
+_dbus_message_loader_pop_message (DBusMessageLoader *loader)
+{
+ return _dbus_list_pop_first (&loader->messages);
+}
+
+/**
+ * Pops a loaded message inside a list link (passing ownership of the
+ * message and link to the caller). Returns #NULL if no messages have
+ * been loaded.
+ *
+ * @param loader the loader.
+ * @returns the next message link, or #NULL if none.
+ */
+DBusList*
+_dbus_message_loader_pop_message_link (DBusMessageLoader *loader)
+{
+ return _dbus_list_pop_first_link (&loader->messages);
+}
+
+/**
+ * Returns a popped message link, used to undo a pop.
+ *
+ * @param loader the loader
+ * @param link the link with a message in it
+ */
+void
+_dbus_message_loader_putback_message_link (DBusMessageLoader *loader,
+ DBusList *link)
+{
+ _dbus_list_prepend_link (&loader->messages, link);
+}
+
+/**
+ * Checks whether the loader is confused due to bad data.
+ * If messages are received that are invalid, the
+ * loader gets confused and gives up permanently.
+ * This state is called "corrupted."
+ *
+ * @param loader the loader
+ * @returns #TRUE if the loader is hosed.
+ */
+dbus_bool_t
+_dbus_message_loader_get_is_corrupted (DBusMessageLoader *loader)
+{
+ _dbus_assert ((loader->corrupted && loader->corruption_reason != DBUS_VALID) ||
+ (!loader->corrupted && loader->corruption_reason == DBUS_VALID));
+ return loader->corrupted;
+}
+
+/**
+ * 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
+ * @param size the max message size in bytes
+ */
+void
+_dbus_message_loader_set_max_message_size (DBusMessageLoader *loader,
+ long size)
+{
+ if (size > DBUS_MAXIMUM_MESSAGE_LENGTH)
+ {
+ _dbus_verbose ("clamping requested max message size %ld to %d\n",
+ size, DBUS_MAXIMUM_MESSAGE_LENGTH);
+ size = DBUS_MAXIMUM_MESSAGE_LENGTH;
+ }
+ loader->max_message_size = size;
+}
+
+/**
+ * Gets the maximum allowed message size in bytes.
+ *
+ * @param loader the loader
+ * @returns max size in bytes
+ */
+long
+_dbus_message_loader_get_max_message_size (DBusMessageLoader *loader)
+{
+ return loader->max_message_size;
+}
+
+/**
+ * Sets the maximum unix fds per message we allow.
+ *
+ * @param loader the loader
+ * @param n the max number of unix fds in a message
+ */
+void
+_dbus_message_loader_set_max_message_unix_fds (DBusMessageLoader *loader,
+ long n)
+{
+ if (n > DBUS_MAXIMUM_MESSAGE_UNIX_FDS)
+ {
+ _dbus_verbose ("clamping requested max message unix_fds %ld to %d\n",
+ n, DBUS_MAXIMUM_MESSAGE_UNIX_FDS);
+ n = DBUS_MAXIMUM_MESSAGE_UNIX_FDS;
+ }
+ loader->max_message_unix_fds = n;
+}
+
+/**
+ * Gets the maximum allowed number of unix fds per message
+ *
+ * @param loader the loader
+ * @returns max unix fds
+ */
+long
+_dbus_message_loader_get_max_message_unix_fds (DBusMessageLoader *loader)
+{
+ return loader->max_message_unix_fds;
+}
+
+/**
+ * Return how many file descriptors are pending in the loader
+ *
+ * @param loader the loader
+ */
+int
+_dbus_message_loader_get_pending_fds_count (DBusMessageLoader *loader)
+{
+#ifdef HAVE_UNIX_FD_PASSING
+ return loader->n_unix_fds;
+#else
+ return 0;
+#endif
+}
+
+/**
+ * Register a function to be called whenever the number of pending file
+ * descriptors in the loader change.
+ *
+ * @param loader the loader
+ * @param callback the callback
+ * @param data the data for the callback
+ */
+void
+_dbus_message_loader_set_pending_fds_function (DBusMessageLoader *loader,
+ void (* callback) (void *),
+ void *data)
+{
+#ifdef HAVE_UNIX_FD_PASSING
+ loader->unix_fds_change = callback;
+ loader->unix_fds_change_data = data;
+#endif
+}
+
+static DBusDataSlotAllocator slot_allocator =
+ _DBUS_DATA_SLOT_ALLOCATOR_INIT (_DBUS_LOCK_NAME (message_slots));
+
+/**
+ * Allocates an integer ID to be used for storing application-specific
+ * data on any DBusMessage. The allocated ID may then be used
+ * with dbus_message_set_data() and dbus_message_get_data().
+ * The passed-in slot must be initialized to -1, and is filled in
+ * with the slot ID. If the passed-in slot is not -1, it's assumed
+ * to be already allocated, and its refcount is incremented.
+ *
+ * The allocated slot is global, i.e. all DBusMessage objects will
+ * have a slot with the given integer ID reserved.
+ *
+ * @param slot_p address of a global variable storing the slot
+ * @returns #FALSE on failure (no memory)
+ */
+dbus_bool_t
+dbus_message_allocate_data_slot (dbus_int32_t *slot_p)
+{
+ return _dbus_data_slot_allocator_alloc (&slot_allocator,
+ slot_p);
+}
+
+/**
+ * Deallocates a global ID for message data slots.
+ * dbus_message_get_data() and dbus_message_set_data() may no
+ * longer be used with this slot. Existing data stored on existing
+ * DBusMessage objects will be freed when the message is
+ * finalized, but may not be retrieved (and may only be replaced if
+ * someone else reallocates the slot). When the refcount on the
+ * passed-in slot reaches 0, it is set to -1.
+ *
+ * @param slot_p address storing the slot to deallocate
+ */
+void
+dbus_message_free_data_slot (dbus_int32_t *slot_p)
+{
+ _dbus_return_if_fail (*slot_p >= 0);
+
+ _dbus_data_slot_allocator_free (&slot_allocator, slot_p);
+}
+
+/**
+ * Stores a pointer on a DBusMessage, along
+ * with an optional function to be used for freeing
+ * the data when the data is set again, or when
+ * the message is finalized. The slot number
+ * must have been allocated with dbus_message_allocate_data_slot().
+ *
+ * @param message the message
+ * @param slot the slot number
+ * @param data the data to store
+ * @param free_data_func finalizer function for the data
+ * @returns #TRUE if there was enough memory to store the data
+ */
+dbus_bool_t
+dbus_message_set_data (DBusMessage *message,
+ dbus_int32_t slot,
+ void *data,
+ DBusFreeFunction free_data_func)
+{
+ DBusFreeFunction old_free_func;
+ void *old_data;
+ dbus_bool_t retval;
+
+ _dbus_return_val_if_fail (message != NULL, FALSE);
+ _dbus_return_val_if_fail (slot >= 0, FALSE);
+
+ retval = _dbus_data_slot_list_set (&slot_allocator,
+ &message->slot_list,
+ slot, data, free_data_func,
+ &old_free_func, &old_data);
+
+ if (retval)
+ {
+ /* Do the actual free outside the message lock */
+ if (old_free_func)
+ (* old_free_func) (old_data);
+ }
+
+ return retval;
+}
+
+/**
+ * Retrieves data previously set with dbus_message_set_data().
+ * The slot must still be allocated (must not have been freed).
+ *
+ * @param message the message
+ * @param slot the slot to get data from
+ * @returns the data, or #NULL if not found
+ */
+void*
+dbus_message_get_data (DBusMessage *message,
+ dbus_int32_t slot)
+{
+ void *res;
+
+ _dbus_return_val_if_fail (message != NULL, NULL);
+
+ res = _dbus_data_slot_list_get (&slot_allocator,
+ &message->slot_list,
+ slot);
+
+ return res;
+}
+
+/**
+ * Utility function to convert a machine-readable (not translated)
+ * string into a D-Bus message type.
+ *
+ * @code
+ * "method_call" -> DBUS_MESSAGE_TYPE_METHOD_CALL
+ * "method_return" -> DBUS_MESSAGE_TYPE_METHOD_RETURN
+ * "signal" -> DBUS_MESSAGE_TYPE_SIGNAL
+ * "error" -> DBUS_MESSAGE_TYPE_ERROR
+ * anything else -> DBUS_MESSAGE_TYPE_INVALID
+ * @endcode
+ *
+ */
+int
+dbus_message_type_from_string (const char *type_str)
+{
+ if (strcmp (type_str, "method_call") == 0)
+ return DBUS_MESSAGE_TYPE_METHOD_CALL;
+ if (strcmp (type_str, "method_return") == 0)
+ return DBUS_MESSAGE_TYPE_METHOD_RETURN;
+ else if (strcmp (type_str, "signal") == 0)
+ return DBUS_MESSAGE_TYPE_SIGNAL;
+ else if (strcmp (type_str, "error") == 0)
+ return DBUS_MESSAGE_TYPE_ERROR;
+ else
+ return DBUS_MESSAGE_TYPE_INVALID;
+}
+
+/**
+ * Utility function to convert a D-Bus message type into a
+ * machine-readable string (not translated).
+ *
+ * @code
+ * DBUS_MESSAGE_TYPE_METHOD_CALL -> "method_call"
+ * DBUS_MESSAGE_TYPE_METHOD_RETURN -> "method_return"
+ * DBUS_MESSAGE_TYPE_SIGNAL -> "signal"
+ * DBUS_MESSAGE_TYPE_ERROR -> "error"
+ * DBUS_MESSAGE_TYPE_INVALID -> "invalid"
+ * @endcode
+ *
+ */
+const char *
+dbus_message_type_to_string (int type)
+{
+ switch (type)
+ {
+ case DBUS_MESSAGE_TYPE_METHOD_CALL:
+ return "method_call";
+ case DBUS_MESSAGE_TYPE_METHOD_RETURN:
+ return "method_return";
+ case DBUS_MESSAGE_TYPE_SIGNAL:
+ return "signal";
+ case DBUS_MESSAGE_TYPE_ERROR:
+ return "error";
+ default:
+ return "invalid";
+ }
+}
+
+/**
+ * Turn a DBusMessage into the marshalled form as described in the D-Bus
+ * specification.
+ *
+ * Generally, this function is only useful for encapsulating D-Bus messages in
+ * a different protocol.
+ *
+ * @param msg the DBusMessage
+ * @param marshalled_data_p the location to save the marshalled form to
+ * @param len_p the location to save the length of the marshalled form to
+ * @returns #FALSE if there was not enough memory
+ */
+dbus_bool_t
+dbus_message_marshal (DBusMessage *msg,
+ char **marshalled_data_p,
+ 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;
+
+ *len_p = _dbus_string_get_length (&tmp);
+
+ if (!_dbus_string_copy (&(msg->body), 0, &tmp, *len_p))
+ goto fail;
+
+ *len_p = _dbus_string_get_length (&tmp);
+
+ if (!_dbus_string_steal_data (&tmp, marshalled_data_p))
+ 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;
+}
+
+/**
+ * Demarshal a D-Bus message from the format described in the D-Bus
+ * specification.
+ *
+ * Generally, this function is only useful for encapsulating D-Bus messages in
+ * a different protocol.
+ *
+ * @param str the marshalled DBusMessage
+ * @param len the length of str
+ * @param error the location to save errors to
+ * @returns #NULL if there was an error
+ */
+DBusMessage *
+dbus_message_demarshal (const char *str,
+ int len,
+ DBusError *error)
+{
+ DBusMessageLoader *loader = NULL;
+ DBusString *buffer;
+ DBusMessage *msg;
+
+ _dbus_return_val_if_fail (str != NULL, NULL);
+
+ loader = _dbus_message_loader_new ();
+
+ if (loader == NULL)
+ goto fail_oom;
+
+ _dbus_message_loader_get_buffer (loader, &buffer, NULL, NULL);
+
+ if (!_dbus_string_append_len (buffer, str, len))
+ goto fail_oom;
+
+ _dbus_message_loader_return_buffer (loader, buffer);
+
+ if (!_dbus_message_loader_queue_messages (loader))
+ goto fail_oom;
+
+ if (_dbus_message_loader_get_is_corrupted (loader))
+ goto fail_corrupt;
+
+ msg = _dbus_message_loader_pop_message (loader);
+
+ if (!msg)
+ goto fail_oom;
+
+ _dbus_message_loader_unref (loader);
+ return msg;
+
+ fail_corrupt:
+ if (loader->corruption_reason == DBUS_VALIDITY_UNKNOWN_OOM_ERROR)
+ goto fail_oom;
+
+ 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;
+
+ fail_oom:
+ _DBUS_SET_OOM (error);
+
+ if (loader != NULL)
+ _dbus_message_loader_unref (loader);
+
+ 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 buf data to be marshalled
+ * @param len the length of @p buf
+ * @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 || (header_len + body_len) > len);
+ (void) have_message; /* unused unless asserting */
+ return header_len + body_len;
+ }
+ else
+ {
+ return -1; /* broken! */
+ }
+}
+
+/**
+ * Sets a flag indicating that the caller of the method is prepared
+ * to wait for interactive authorization to take place (for instance
+ * via Polkit) before the actual method is processed.
+ *
+ * The flag is #FALSE by default; that is, by default the other end is
+ * expected to make any authorization decisions non-interactively
+ * and promptly. It may use the error
+ * #DBUS_ERROR_INTERACTIVE_AUTHORIZATION_REQUIRED to signal that
+ * authorization failed, but could have succeeded if this flag had
+ * been used.
+ *
+ * For messages whose type is not #DBUS_MESSAGE_TYPE_METHOD_CALL,
+ * this flag is meaningless and should not be set.
+ *
+ * On the protocol level this toggles
+ * #DBUS_HEADER_FLAG_ALLOW_INTERACTIVE_AUTHORIZATION.
+ *
+ * @param message the message
+ * @param allow #TRUE if interactive authorization is acceptable
+ */
+void
+dbus_message_set_allow_interactive_authorization (DBusMessage *message,
+ dbus_bool_t allow)
+{
+ _dbus_return_if_fail (message != NULL);
+ _dbus_return_if_fail (!message->locked);
+
+ _dbus_header_toggle_flag (&message->header,
+ DBUS_HEADER_FLAG_ALLOW_INTERACTIVE_AUTHORIZATION,
+ allow);
+}
+
+/**
+ * Returns whether the flag controlled by
+ * dbus_message_set_allow_interactive_authorization() has been set.
+ *
+ * @param message the message
+ */
+dbus_bool_t
+dbus_message_get_allow_interactive_authorization (DBusMessage *message)
+{
+ _dbus_return_val_if_fail (message != NULL, FALSE);
+
+ return _dbus_header_get_flag (&message->header,
+ DBUS_HEADER_FLAG_ALLOW_INTERACTIVE_AUTHORIZATION);
+}
+
+/**
+ * An opaque data structure containing the serialized form of any single
+ * D-Bus message item, whose signature is a single complete type.
+ *
+ * (Implementation detail: It's serialized as a single variant.)
+ */
+struct DBusVariant
+{
+ DBusString data;
+};
+
+/**
+ * Copy a single D-Bus message item from reader into a
+ * newly-allocated #DBusVariant.
+ *
+ * For example, if a message contains three string arguments, and reader points
+ * to the second string, the resulting DBusVariant will have signature
+ * #DBUS_TYPE_STRING_AS_STRING and contain only that second string.
+ *
+ * @param reader An iterator over message items, pointing to one item to copy
+ * @returns The variant, or #NULL if out of memory
+ */
+DBusVariant *
+_dbus_variant_read (DBusMessageIter *reader)
+{
+ DBusVariant *self = NULL;
+ /* Points to the single item we will read from the reader */
+ DBusMessageRealIter *real_reader = (DBusMessageRealIter *) reader;
+ /* The position in self at which we will write a single variant
+ * (it is position 0) */
+ DBusTypeWriter items_writer;
+ /* The position in self at which we will write a copy of reader
+ * (it is inside the variant) */
+ DBusTypeWriter variant_writer;
+ /* 'v' */
+ DBusString variant_signature;
+ /* Whatever is the signature of the item we will copy from the reader */
+ DBusString contained_signature;
+ /* TRUE if self->data needs to be freed */
+ dbus_bool_t data_inited = FALSE;
+ /* The type of the item we will read from the reader */
+ int type;
+ /* The string, start position within that string, and length of the signature
+ * of the single complete type of the item reader points to */
+ const DBusString *sig;
+ int start, len;
+
+ _dbus_assert (_dbus_message_iter_check (real_reader));
+ _dbus_assert (real_reader->iter_type == DBUS_MESSAGE_ITER_TYPE_READER);
+ _dbus_string_init_const (&variant_signature, DBUS_TYPE_VARIANT_AS_STRING);
+ type = dbus_message_iter_get_arg_type (reader);
+ _dbus_type_reader_get_signature (&real_reader->u.reader, &sig, &start, &len);
+
+ if (!_dbus_string_init (&contained_signature))
+ return NULL;
+
+ if (!_dbus_string_copy_len (sig, start, len, &contained_signature, 0))
+ goto oom;
+
+ self = dbus_new0 (DBusVariant, 1);
+
+ if (self == NULL)
+ goto oom;
+
+ if (!_dbus_string_init (&self->data))
+ goto oom;
+
+ data_inited = TRUE;
+
+ _dbus_type_writer_init_values_only (&items_writer, DBUS_COMPILER_BYTE_ORDER,
+ &variant_signature, 0, &self->data, 0);
+
+ if (!_dbus_type_writer_recurse (&items_writer, DBUS_TYPE_VARIANT,
+ &contained_signature, 0, &variant_writer))
+ goto oom;
+
+ if (type == DBUS_TYPE_ARRAY)
+ {
+ /* Points to each item in turn inside the array we are copying */
+ DBusMessageIter array_reader;
+ /* Same as array_reader */
+ DBusMessageRealIter *real_array_reader = (DBusMessageRealIter *) &array_reader;
+ /* The position inside the copied array at which we will write
+ * the copy of array_reader */
+ DBusTypeWriter array_writer;
+
+ dbus_message_iter_recurse (reader, &array_reader);
+
+ if (!_dbus_type_writer_recurse (&variant_writer, type,
+ &contained_signature, 1, &array_writer))
+ goto oom;
+
+ if (!_dbus_type_writer_write_reader (&array_writer,
+ &real_array_reader->u.reader))
+ goto oom;
+
+ if (!_dbus_type_writer_unrecurse (&variant_writer, &array_writer))
+ goto oom;
+ }
+ else if (type == DBUS_TYPE_DICT_ENTRY || type == DBUS_TYPE_VARIANT ||
+ type == DBUS_TYPE_STRUCT)
+ {
+ /* Points to each item in turn inside the container we are copying */
+ DBusMessageIter inner_reader;
+ /* Same as inner_reader */
+ DBusMessageRealIter *real_inner_reader = (DBusMessageRealIter *) &inner_reader;
+ /* The position inside the copied container at which we will write the
+ * copy of inner_reader */
+ DBusTypeWriter inner_writer;
+
+ dbus_message_iter_recurse (reader, &inner_reader);
+
+ if (!_dbus_type_writer_recurse (&variant_writer, type, NULL, 0,
+ &inner_writer))
+ goto oom;
+
+ if (!_dbus_type_writer_write_reader (&inner_writer,
+ &real_inner_reader->u.reader))
+ goto oom;
+
+ if (!_dbus_type_writer_unrecurse (&variant_writer, &inner_writer))
+ goto oom;
+ }
+ else
+ {
+ DBusBasicValue value;
+
+ /* We eliminated all the container types above */
+ _dbus_assert (dbus_type_is_basic (type));
+
+ dbus_message_iter_get_basic (reader, &value);
+
+ if (!_dbus_type_writer_write_basic (&variant_writer, type, &value))
+ goto oom;
+ }
+
+ _dbus_string_free (&contained_signature);
+ return self;
+
+oom:
+ if (self != NULL)
+ {
+ if (data_inited)
+ _dbus_string_free (&self->data);
+
+ dbus_free (self);
+ }
+
+ _dbus_string_free (&contained_signature);
+ return NULL;
+}
+
+/**
+ * Return the signature of the item stored in self. It is a single complete
+ * type.
+ *
+ * @param self the variant
+ */
+const char *
+_dbus_variant_get_signature (DBusVariant *self)
+{
+ const char *ret;
+#ifndef DBUS_DISABLE_ASSERT
+ unsigned char len;
+#endif
+
+ _dbus_assert (self != NULL);
+
+#ifndef DBUS_DISABLE_ASSERT
+ /* Here we make use of the fact that the serialization of a variant starts
+ * with the 1-byte length, then that many bytes of signature, then \0. */
+ len = _dbus_string_get_byte (&self->data, 0);
+#endif
+ ret = _dbus_string_get_const_data_len (&self->data, 1, len);
+ _dbus_assert (strlen (ret) == len);
+ return ret;
+}
+
+/**
+ * Copy the single D-Bus message item from self into writer.
+ *
+ * For example, if writer points into the body of an empty message and self has
+ * signature #DBUS_TYPE_STRING_AS_STRING, then the message will
+ * have signature #DBUS_TYPE_STRING_AS_STRING after this function returns
+ *
+ * @param self the variant
+ * @param writer the place to write the contents of the variant
+ * @returns #TRUE on success, #FALSE if out of memory
+ */
+dbus_bool_t
+_dbus_variant_write (DBusVariant *self,
+ DBusMessageIter *writer)
+{
+ /* 'v' */
+ DBusString variant_signature;
+ /* Points to the single item in self */
+ DBusTypeReader variant_reader;
+ /* Points to the single item (of whatever type) inside the variant */
+ DBusTypeReader reader;
+ /* The position at which we will copy reader */
+ DBusMessageRealIter *real_writer = (DBusMessageRealIter *) writer;
+ dbus_bool_t ret;
+
+ _dbus_assert (self != NULL);
+ _dbus_assert (_dbus_message_iter_append_check (real_writer));
+ _dbus_assert (real_writer->iter_type == DBUS_MESSAGE_ITER_TYPE_WRITER);
+
+ _dbus_string_init_const (&variant_signature, DBUS_TYPE_VARIANT_AS_STRING);
+ _dbus_type_reader_init (&reader, DBUS_COMPILER_BYTE_ORDER,
+ &variant_signature, 0, &self->data, 0);
+ _dbus_type_reader_recurse (&reader, &variant_reader);
+
+ if (!_dbus_message_iter_open_signature (real_writer))
+ return FALSE;
+
+ ret = _dbus_type_writer_write_reader (&real_writer->u.writer,
+ &variant_reader);
+
+ if (!_dbus_message_iter_close_signature (real_writer))
+ return FALSE;
+
+ return ret;
+}
+
+int
+_dbus_variant_get_length (DBusVariant *self)
+{
+ _dbus_assert (self != NULL);
+ return _dbus_string_get_length (&self->data);
+}
+
+const DBusString *
+_dbus_variant_peek (DBusVariant *self)
+{
+ _dbus_assert (self != NULL);
+ return &self->data;
+}
+
+void
+_dbus_variant_free (DBusVariant *self)
+{
+ _dbus_assert (self != NULL);
+ _dbus_string_free (&self->data);
+ dbus_free (self);
+}
+
+/** @} */
+
+/* tests in dbus-message-util.c */
diff --git a/src/3rdparty/libdbus/dbus/dbus-message.h b/src/3rdparty/libdbus/dbus/dbus-message.h
new file mode 100644
index 00000000..0bbad3d1
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-message.h
@@ -0,0 +1,401 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-message.h DBusMessage object
+ *
+ * Copyright (C) 2002, 2003, 2005 Red Hat Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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
+ *
+ */
+#if !defined (DBUS_INSIDE_DBUS_H) && !defined (DBUS_COMPILATION)
+#error "Only <dbus/dbus.h> can be included directly, this file may disappear or change contents."
+#endif
+
+#ifndef DBUS_MESSAGE_H
+#define DBUS_MESSAGE_H
+
+#include <dbus/dbus-macros.h>
+#include <dbus/dbus-types.h>
+#include <dbus/dbus-arch-deps.h>
+#include <dbus/dbus-memory.h>
+#include <dbus/dbus-errors.h>
+#include <stdarg.h>
+
+DBUS_BEGIN_DECLS
+
+/**
+ * @addtogroup DBusMessage
+ * @{
+ */
+
+typedef struct DBusMessage DBusMessage;
+/**
+ * Opaque type representing a message iterator. Can be copied by value and
+ * allocated on the stack.
+ *
+ * A DBusMessageIter usually contains no allocated memory. However, there
+ * is one special case: after a successful call to
+ * dbus_message_iter_open_container(), the caller is responsible for calling
+ * either dbus_message_iter_close_container() or
+ * dbus_message_iter_abandon_container() exactly once, with the same pair
+ * of iterators.
+ */
+typedef struct DBusMessageIter DBusMessageIter;
+
+/**
+ * DBusMessageIter struct; contains no public fields.
+ */
+struct DBusMessageIter
+{
+#if DBUS_SIZEOF_VOID_P > 8
+ void *dummy[16]; /**< Don't use this */
+#else
+ void *dummy1; /**< Don't use this */
+ void *dummy2; /**< Don't use this */
+ dbus_uint32_t dummy3; /**< Don't use this */
+ int dummy4; /**< Don't use this */
+ int dummy5; /**< Don't use this */
+ int dummy6; /**< Don't use this */
+ int dummy7; /**< Don't use this */
+ int dummy8; /**< Don't use this */
+ int dummy9; /**< Don't use this */
+ int dummy10; /**< Don't use this */
+ int dummy11; /**< Don't use this */
+ int pad1; /**< Don't use this */
+ void *pad2; /**< Don't use this */
+ void *pad3; /**< Don't use this */
+#endif
+};
+
+/**
+ * A message iterator for which dbus_message_iter_abandon_container_if_open()
+ * is the only valid operation.
+ */
+#if DBUS_SIZEOF_VOID_P > 8
+#define DBUS_MESSAGE_ITER_INIT_CLOSED \
+{ \
+ { \
+ NULL, NULL, NULL, NULL, \
+ NULL, NULL, NULL, NULL, \
+ NULL, NULL, NULL, NULL, \
+ NULL, NULL, NULL, NULL \
+ } \
+}
+#else
+#define DBUS_MESSAGE_ITER_INIT_CLOSED \
+{ \
+ NULL, /* dummy1 */ \
+ NULL, /* dummy2 */ \
+ 0, /* dummy3 */ \
+ 0, /* dummy4 */ \
+ 0, /* dummy5 */ \
+ 0, /* dummy6 */ \
+ 0, /* dummy7 */ \
+ 0, /* dummy8 */ \
+ 0, /* dummy9 */ \
+ 0, /* dummy10 */ \
+ 0, /* dummy11 */ \
+ 0, /* pad1 */ \
+ NULL, /* pad2 */ \
+ NULL /* pad3 */ \
+}
+#endif
+
+DBUS_EXPORT
+DBusMessage* dbus_message_new (int message_type);
+DBUS_EXPORT
+DBusMessage* dbus_message_new_method_call (const char *bus_name,
+ const char *path,
+ const char *iface,
+ const char *method);
+DBUS_EXPORT
+DBusMessage* dbus_message_new_method_return (DBusMessage *method_call);
+DBUS_EXPORT
+DBusMessage* dbus_message_new_signal (const char *path,
+ const char *iface,
+ const char *name);
+DBUS_EXPORT
+DBusMessage* dbus_message_new_error (DBusMessage *reply_to,
+ const char *error_name,
+ const char *error_message);
+DBUS_EXPORT
+DBusMessage* dbus_message_new_error_printf (DBusMessage *reply_to,
+ const char *error_name,
+ const char *error_format,
+ ...) _DBUS_GNUC_PRINTF (3, 4);
+
+DBUS_EXPORT
+DBusMessage* dbus_message_copy (const DBusMessage *message);
+
+DBUS_EXPORT
+DBusMessage* dbus_message_ref (DBusMessage *message);
+DBUS_EXPORT
+void dbus_message_unref (DBusMessage *message);
+DBUS_EXPORT
+int dbus_message_get_type (DBusMessage *message);
+DBUS_EXPORT
+dbus_bool_t dbus_message_set_path (DBusMessage *message,
+ const char *object_path);
+DBUS_EXPORT
+const char* dbus_message_get_path (DBusMessage *message);
+DBUS_EXPORT
+dbus_bool_t dbus_message_has_path (DBusMessage *message,
+ const char *object_path);
+DBUS_EXPORT
+dbus_bool_t dbus_message_set_interface (DBusMessage *message,
+ const char *iface);
+DBUS_EXPORT
+const char* dbus_message_get_interface (DBusMessage *message);
+DBUS_EXPORT
+dbus_bool_t dbus_message_has_interface (DBusMessage *message,
+ const char *iface);
+DBUS_EXPORT
+dbus_bool_t dbus_message_set_member (DBusMessage *message,
+ const char *member);
+DBUS_EXPORT
+const char* dbus_message_get_member (DBusMessage *message);
+DBUS_EXPORT
+dbus_bool_t dbus_message_has_member (DBusMessage *message,
+ const char *member);
+DBUS_EXPORT
+dbus_bool_t dbus_message_set_error_name (DBusMessage *message,
+ const char *name);
+DBUS_EXPORT
+const char* dbus_message_get_error_name (DBusMessage *message);
+DBUS_EXPORT
+dbus_bool_t dbus_message_set_destination (DBusMessage *message,
+ const char *destination);
+DBUS_EXPORT
+const char* dbus_message_get_destination (DBusMessage *message);
+DBUS_EXPORT
+dbus_bool_t dbus_message_set_sender (DBusMessage *message,
+ const char *sender);
+DBUS_EXPORT
+const char* dbus_message_get_sender (DBusMessage *message);
+DBUS_EXPORT
+const char* dbus_message_get_signature (DBusMessage *message);
+DBUS_EXPORT
+void dbus_message_set_no_reply (DBusMessage *message,
+ dbus_bool_t no_reply);
+DBUS_EXPORT
+dbus_bool_t dbus_message_get_no_reply (DBusMessage *message);
+DBUS_EXPORT
+dbus_bool_t dbus_message_is_method_call (DBusMessage *message,
+ const char *iface,
+ const char *method);
+DBUS_EXPORT
+dbus_bool_t dbus_message_is_signal (DBusMessage *message,
+ const char *iface,
+ const char *signal_name);
+DBUS_EXPORT
+dbus_bool_t dbus_message_is_error (DBusMessage *message,
+ const char *error_name);
+DBUS_EXPORT
+dbus_bool_t dbus_message_has_destination (DBusMessage *message,
+ const char *bus_name);
+DBUS_EXPORT
+dbus_bool_t dbus_message_has_sender (DBusMessage *message,
+ const char *unique_bus_name);
+DBUS_EXPORT
+dbus_bool_t dbus_message_has_signature (DBusMessage *message,
+ const char *signature);
+DBUS_EXPORT
+dbus_uint32_t dbus_message_get_serial (DBusMessage *message);
+DBUS_EXPORT
+void dbus_message_set_serial (DBusMessage *message,
+ dbus_uint32_t serial);
+DBUS_EXPORT
+dbus_bool_t dbus_message_set_reply_serial (DBusMessage *message,
+ dbus_uint32_t reply_serial);
+DBUS_EXPORT
+dbus_uint32_t dbus_message_get_reply_serial (DBusMessage *message);
+
+DBUS_EXPORT
+void dbus_message_set_auto_start (DBusMessage *message,
+ dbus_bool_t auto_start);
+DBUS_EXPORT
+dbus_bool_t dbus_message_get_auto_start (DBusMessage *message);
+
+DBUS_EXPORT
+dbus_bool_t dbus_message_get_path_decomposed (DBusMessage *message,
+ char ***path);
+
+DBUS_EXPORT
+const char *dbus_message_get_container_instance (DBusMessage *message);
+DBUS_EXPORT
+dbus_bool_t dbus_message_set_container_instance (DBusMessage *message,
+ const char *object_path);
+
+DBUS_EXPORT
+dbus_bool_t dbus_message_append_args (DBusMessage *message,
+ int first_arg_type,
+ ...);
+DBUS_EXPORT
+dbus_bool_t dbus_message_append_args_valist (DBusMessage *message,
+ int first_arg_type,
+ va_list var_args);
+DBUS_EXPORT
+dbus_bool_t dbus_message_get_args (DBusMessage *message,
+ DBusError *error,
+ int first_arg_type,
+ ...);
+DBUS_EXPORT
+dbus_bool_t dbus_message_get_args_valist (DBusMessage *message,
+ DBusError *error,
+ int first_arg_type,
+ va_list var_args);
+
+DBUS_EXPORT
+dbus_bool_t dbus_message_contains_unix_fds (DBusMessage *message);
+
+DBUS_EXPORT
+void dbus_message_iter_init_closed (DBusMessageIter *iter);
+DBUS_EXPORT
+dbus_bool_t dbus_message_iter_init (DBusMessage *message,
+ DBusMessageIter *iter);
+DBUS_EXPORT
+dbus_bool_t dbus_message_iter_has_next (DBusMessageIter *iter);
+DBUS_EXPORT
+dbus_bool_t dbus_message_iter_next (DBusMessageIter *iter);
+DBUS_EXPORT
+char* dbus_message_iter_get_signature (DBusMessageIter *iter);
+DBUS_EXPORT
+int dbus_message_iter_get_arg_type (DBusMessageIter *iter);
+DBUS_EXPORT
+int dbus_message_iter_get_element_type (DBusMessageIter *iter);
+DBUS_EXPORT
+void dbus_message_iter_recurse (DBusMessageIter *iter,
+ DBusMessageIter *sub);
+DBUS_EXPORT
+void dbus_message_iter_get_basic (DBusMessageIter *iter,
+ void *value);
+DBUS_EXPORT
+int dbus_message_iter_get_element_count(DBusMessageIter *iter);
+
+#ifndef DBUS_DISABLE_DEPRECATED
+/* This function returns the wire protocol size of the array in bytes,
+ * you do not want to know that probably
+ */
+DBUS_EXPORT
+DBUS_DEPRECATED int dbus_message_iter_get_array_len (DBusMessageIter *iter);
+#endif
+DBUS_EXPORT
+void dbus_message_iter_get_fixed_array (DBusMessageIter *iter,
+ void *value,
+ int *n_elements);
+
+
+DBUS_EXPORT
+void dbus_message_iter_init_append (DBusMessage *message,
+ DBusMessageIter *iter);
+DBUS_EXPORT
+dbus_bool_t dbus_message_iter_append_basic (DBusMessageIter *iter,
+ int type,
+ const void *value);
+DBUS_EXPORT
+dbus_bool_t dbus_message_iter_append_fixed_array (DBusMessageIter *iter,
+ int element_type,
+ const void *value,
+ int n_elements);
+DBUS_EXPORT
+dbus_bool_t dbus_message_iter_open_container (DBusMessageIter *iter,
+ int type,
+ const char *contained_signature,
+ DBusMessageIter *sub);
+DBUS_EXPORT
+dbus_bool_t dbus_message_iter_close_container (DBusMessageIter *iter,
+ DBusMessageIter *sub);
+DBUS_EXPORT
+void dbus_message_iter_abandon_container (DBusMessageIter *iter,
+ DBusMessageIter *sub);
+
+DBUS_EXPORT
+void dbus_message_iter_abandon_container_if_open (DBusMessageIter *iter,
+ DBusMessageIter *sub);
+
+DBUS_EXPORT
+void dbus_message_lock (DBusMessage *message);
+
+DBUS_EXPORT
+dbus_bool_t dbus_set_error_from_message (DBusError *error,
+ DBusMessage *message);
+
+
+DBUS_EXPORT
+dbus_bool_t dbus_message_allocate_data_slot (dbus_int32_t *slot_p);
+DBUS_EXPORT
+void dbus_message_free_data_slot (dbus_int32_t *slot_p);
+DBUS_EXPORT
+dbus_bool_t dbus_message_set_data (DBusMessage *message,
+ dbus_int32_t slot,
+ void *data,
+ DBusFreeFunction free_data_func);
+DBUS_EXPORT
+void* dbus_message_get_data (DBusMessage *message,
+ dbus_int32_t slot);
+
+DBUS_EXPORT
+int dbus_message_type_from_string (const char *type_str);
+DBUS_EXPORT
+const char* dbus_message_type_to_string (int type);
+
+DBUS_EXPORT
+dbus_bool_t dbus_message_marshal (DBusMessage *msg,
+ char **marshalled_data_p,
+ int *len_p);
+DBUS_EXPORT
+DBusMessage* dbus_message_demarshal (const char *str,
+ int len,
+ DBusError *error);
+
+DBUS_EXPORT
+int dbus_message_demarshal_bytes_needed (const char *str,
+ int len);
+
+DBUS_EXPORT
+void dbus_message_set_allow_interactive_authorization (DBusMessage *message,
+ dbus_bool_t allow);
+
+DBUS_EXPORT
+dbus_bool_t dbus_message_get_allow_interactive_authorization (
+ DBusMessage *message);
+
+/**
+ * Clear a variable or struct member that contains a #DBusMessage.
+ * If it does not contain #NULL, the message that was previously
+ * there is unreferenced with dbus_message_unref().
+ *
+ * This is very similar to dbus_clear_connection(): see that function
+ * for more details.
+ *
+ * @param pointer_to_message A pointer to a variable or struct member.
+ * pointer_to_message must not be #NULL, but *pointer_to_message
+ * may be #NULL.
+ */
+static inline void
+dbus_clear_message (DBusMessage **pointer_to_message)
+{
+ _dbus_clear_pointer_impl (DBusMessage, pointer_to_message,
+ dbus_message_unref);
+}
+
+/** @} */
+
+DBUS_END_DECLS
+
+#endif /* DBUS_MESSAGE_H */
diff --git a/src/3rdparty/libdbus/dbus/dbus-misc.c b/src/3rdparty/libdbus/dbus/dbus-misc.c
new file mode 100644
index 00000000..0f6c2e6d
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-misc.c
@@ -0,0 +1,226 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-misc.c A few assorted public functions that don't fit elsewhere
+ *
+ * Copyright (C) 2006 Red Hat, Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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-misc.h"
+#include "dbus-internals.h"
+#include "dbus-string.h"
+#include <dbus/dbus-test-tap.h>
+
+/**
+ * @defgroup DBusMisc Miscellaneous
+ * @ingroup DBus
+ * @brief Miscellaneous API that doesn't cleanly fit anywhere else
+ *
+ * @{
+ */
+
+/**
+ * Obtains the machine UUID of the machine this process is running on.
+ *
+ * The returned string must be freed with dbus_free().
+ *
+ * This UUID is guaranteed to remain the same until the next reboot
+ * (unless the sysadmin foolishly changes it and screws themselves).
+ * It will usually remain the same across reboots also, but hardware
+ * configuration changes or rebuilding the machine could break that.
+ *
+ * The idea is that two processes with the same machine ID should be
+ * able to use shared memory, UNIX domain sockets, process IDs, and other
+ * features of the OS that require both processes to be running
+ * on the same OS kernel instance.
+ *
+ * The machine ID can also be used to create unique per-machine
+ * instances. For example, you could use it in bus names or
+ * X selection names.
+ *
+ * The machine ID is preferred over the machine hostname, because
+ * the hostname is frequently set to "localhost.localdomain" and
+ * may also change at runtime.
+ *
+ * You can get the machine ID of a remote application by invoking the
+ * method GetMachineId from interface org.freedesktop.DBus.Peer.
+ *
+ * If the remote application has the same machine ID as the one
+ * returned by this function, then the remote application is on the
+ * same machine as your application.
+ *
+ * The UUID is not a UUID in the sense of RFC4122; the details
+ * are explained in the D-Bus specification.
+ *
+ * @returns a 32-byte-long hex-encoded UUID string, or #NULL on failure
+ */
+char *
+dbus_try_get_local_machine_id (DBusError *error)
+{
+ DBusString uuid;
+ char *s;
+
+ s = NULL;
+
+ if (!_dbus_string_init (&uuid))
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ return NULL;
+ }
+
+ if (!_dbus_get_local_machine_uuid_encoded (&uuid, error))
+ {
+ _dbus_string_free (&uuid);
+ return NULL;
+ }
+
+ if (!_dbus_string_steal_data (&uuid, &s))
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ _dbus_string_free (&uuid);
+ return NULL;
+ }
+ else
+ {
+ _dbus_string_free (&uuid);
+ return s;
+ }
+
+}
+
+/**
+ * Obtains the machine UUID of the machine this process is running on.
+ *
+ * The returned string must be freed with dbus_free().
+ *
+ * This function returns #NULL if there was not enough memory to read
+ * the UUID, or if the UUID could not be read because the D-Bus
+ * library was installed incorrectly. In the latter case, a warning
+ * is logged.
+ *
+ * Other than its deficient error reporting, this function is the same as
+ * dbus_try_get_local_machine_id().
+ *
+ * @returns a 32-byte-long hex-encoded UUID string, or #NULL on failure
+ */
+char *
+dbus_get_local_machine_id (void)
+{
+ DBusError error = DBUS_ERROR_INIT;
+ char *s;
+
+ s = dbus_try_get_local_machine_id (&error);
+
+ /* The documentation says dbus_get_local_machine_id() only fails on OOM;
+ * this can actually also fail if the D-Bus installation is faulty
+ * (no UUID), but we have no good way to report that. Historically,
+ * _dbus_get_local_machine_uuid_encoded was responsible for issuing the
+ * warning; now we do that here. */
+ if (s == NULL)
+ {
+ if (!dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
+ _dbus_warn_check_failed ("%s", error.message);
+
+ dbus_error_free (&error);
+ return NULL;
+ }
+
+ return s;
+}
+
+/**
+ * @def DBUS_MAJOR_VERSION
+ *
+ * The COMPILE TIME major version of libdbus, that is, the "X" in "X.Y.Z",
+ * as an integer literal. Consider carefully whether to use this or the
+ * runtime version from dbus_get_version().
+ */
+
+/**
+ * @def DBUS_MINOR_VERSION
+ *
+ * The COMPILE TIME minor version of libdbus, that is, the "Y" in "X.Y.Z",
+ * as an integer literal. Consider carefully whether to use this or the
+ * runtime version from dbus_get_version().
+ */
+
+/**
+ * @def DBUS_MICRO_VERSION
+ *
+ * The COMPILE TIME micro version of libdbus, that is, the "Z" in "X.Y.Z",
+ * as an integer literal. Consider carefully whether to use this or the
+ * runtime version from dbus_get_version().
+ */
+
+/**
+ * @def DBUS_VERSION
+ *
+ * The COMPILE TIME version of libdbus, as a single integer that has 0 in the most
+ * significant byte, the major version in the next most significant byte,
+ * the minor version in the third most significant, and the micro version in the
+ * least significant byte. This means two DBUS_VERSION can be compared to see
+ * which is higher.
+ *
+ * Consider carefully whether to use this or the runtime version from
+ * dbus_get_version().
+ */
+
+/**
+ * @def DBUS_VERSION_STRING
+ *
+ * The COMPILE TIME version of libdbus, as a string "X.Y.Z".
+ *
+ * Consider carefully whether to use this or the runtime version from
+ * dbus_get_version().
+ */
+
+/**
+ * Gets the DYNAMICALLY LINKED version of libdbus. Alternatively, there
+ * are macros #DBUS_MAJOR_VERSION, #DBUS_MINOR_VERSION, #DBUS_MICRO_VERSION,
+ * and #DBUS_VERSION which allow you to test the VERSION YOU ARE COMPILED AGAINST.
+ * In other words, you can get either the runtime or the compile-time version.
+ * Think carefully about which of these you want in a given case.
+ *
+ * The libdbus full version number is "MAJOR.MINOR.MICRO" where the
+ * MINOR changes if API is added, and the MICRO changes with each
+ * release of a MAJOR.MINOR series. The MINOR is an odd number for
+ * development releases and an even number for stable releases.
+ *
+ * @param major_version_p pointer to return the major version, or #NULL
+ * @param minor_version_p pointer to return the minor version, or #NULL
+ * @param micro_version_p pointer to return the micro version, or #NULL
+ *
+ */
+void
+dbus_get_version (int *major_version_p,
+ int *minor_version_p,
+ int *micro_version_p)
+{
+ if (major_version_p)
+ *major_version_p = DBUS_MAJOR_VERSION;
+ if (minor_version_p)
+ *minor_version_p = DBUS_MINOR_VERSION;
+ if (micro_version_p)
+ *micro_version_p = DBUS_MICRO_VERSION;
+}
+
+
+/** @} */ /* End of public API */
diff --git a/src/3rdparty/libdbus/dbus/dbus-misc.h b/src/3rdparty/libdbus/dbus/dbus-misc.h
new file mode 100644
index 00000000..3442ab78
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-misc.h
@@ -0,0 +1,60 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-misc.h A few assorted public functions that don't fit elsewhere
+ *
+ * Copyright (C) 2006 Red Hat, Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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
+ *
+ */
+#if !defined (DBUS_INSIDE_DBUS_H) && !defined (DBUS_COMPILATION)
+#error "Only <dbus/dbus.h> can be included directly, this file may disappear or change contents."
+#endif
+
+#ifndef DBUS_MISC_H
+#define DBUS_MISC_H
+
+#include <dbus/dbus-types.h>
+#include <dbus/dbus-errors.h>
+
+DBUS_BEGIN_DECLS
+
+/**
+ * @addtogroup DBusMisc
+ * @{
+ */
+DBUS_EXPORT
+char* dbus_get_local_machine_id (void);
+
+DBUS_EXPORT
+void dbus_get_version (int *major_version_p,
+ int *minor_version_p,
+ int *micro_version_p);
+
+DBUS_EXPORT
+dbus_bool_t dbus_setenv (const char *variable,
+ const char *value);
+
+DBUS_EXPORT
+char *dbus_try_get_local_machine_id (DBusError *error);
+
+/** @} */
+
+DBUS_END_DECLS
+
+#endif /* DBUS_MISC_H */
diff --git a/src/3rdparty/libdbus/dbus/dbus-nonce.c b/src/3rdparty/libdbus/dbus/dbus-nonce.c
new file mode 100644
index 00000000..5ac9a013
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-nonce.c
@@ -0,0 +1,531 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-nonce.c Nonce handling functions used by nonce-tcp (internal to D-Bus implementation)
+ *
+ * Copyright (C) 2009 Klaralvdalens Datakonsult AB, a KDAB Group company, info@kdab.net
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <config.h>
+// major sections of this file are modified code from libassuan, (C) FSF
+#include "dbus-nonce.h"
+#include "dbus-internals.h"
+#include "dbus-protocol.h"
+#include "dbus-sysdeps.h"
+
+#include <stdio.h>
+
+struct DBusNonceFile
+{
+ DBusString path;
+ DBusString dir;
+};
+
+static dbus_bool_t
+do_check_nonce (DBusSocket fd, const DBusString *nonce, DBusError *error)
+{
+ DBusString buffer;
+ DBusString p;
+ size_t nleft;
+ dbus_bool_t result;
+ int n;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ nleft = 16;
+
+ /* This is a trick to make it safe to call _dbus_string_free on these
+ * strings during error unwinding, even if allocating memory for them
+ * fails. A constant DBusString is considered to be valid to "free",
+ * even though there is nothing to free (of course the free operation
+ * is trivial, because it does not own its own buffer); but
+ * unlike a mutable DBusString, initializing a constant DBusString
+ * cannot fail.
+ *
+ * We must successfully re-initialize the strings to be mutable before
+ * writing to them, of course.
+ */
+ _dbus_string_init_const (&buffer, "");
+ _dbus_string_init_const (&p, "");
+
+ if ( !_dbus_string_init (&buffer)
+ || !_dbus_string_init (&p) ) {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ _dbus_string_free (&p);
+ _dbus_string_free (&buffer);
+ return FALSE;
+ }
+
+ while (nleft)
+ {
+ int saved_errno;
+
+ n = _dbus_read_socket (fd, &p, nleft);
+ saved_errno = _dbus_save_socket_errno ();
+
+ if (n == -1 && _dbus_get_is_errno_eintr (saved_errno))
+ ;
+ else if (n == -1 && _dbus_get_is_errno_eagain_or_ewouldblock (saved_errno))
+ _dbus_sleep_milliseconds (100);
+ else if (n==-1)
+ {
+ dbus_set_error (error, DBUS_ERROR_IO_ERROR, "Could not read nonce from socket (fd=%" DBUS_SOCKET_FORMAT ")", _dbus_socket_printable (fd));
+ _dbus_string_free (&p);
+ _dbus_string_free (&buffer);
+ return FALSE;
+ }
+ else if (!n)
+ {
+ _dbus_string_free (&p);
+ _dbus_string_free (&buffer);
+ dbus_set_error (error, DBUS_ERROR_IO_ERROR, "Could not read nonce from socket (fd=%" DBUS_SOCKET_FORMAT ")", _dbus_socket_printable (fd));
+ return FALSE;
+ }
+ else
+ {
+ if (!_dbus_string_append_len (&buffer, _dbus_string_get_const_data (&p), n))
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ _dbus_string_free (&p);
+ _dbus_string_free (&buffer);
+ return FALSE;
+ }
+ nleft -= n;
+ }
+ }
+
+ result = _dbus_string_equal_len (&buffer, nonce, 16);
+ if (!result)
+ dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED, "Nonces do not match, access denied (fd=%" DBUS_SOCKET_FORMAT ")", _dbus_socket_printable (fd));
+
+ _dbus_string_free (&p);
+ _dbus_string_free (&buffer);
+
+ return result;
+}
+
+/**
+ * reads the nonce from the nonce file and stores it in a string
+ *
+ * @param fname the file to read the nonce from
+ * @param nonce returns the nonce. Must be an initialized string, the nonce will be appended.
+ * @param error error object to report possible errors
+ * @return FALSE iff reading the nonce fails (error is set then)
+ */
+dbus_bool_t
+_dbus_read_nonce (const DBusString *fname, DBusString *nonce, DBusError* error)
+{
+ FILE *fp;
+ char buffer[17];
+ size_t nread;
+
+ buffer[sizeof buffer - 1] = '\0';
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ _dbus_verbose ("reading nonce from file: %s\n", _dbus_string_get_const_data (fname));
+
+
+ fp = fopen (_dbus_string_get_const_data (fname), "rb");
+ if (!fp)
+ {
+ dbus_set_error (error,
+ _dbus_error_from_system_errno (),
+ "Failed to open %s for read: %s",
+ _dbus_string_get_const_data (fname),
+ _dbus_strerror_from_errno ());
+ return FALSE;
+ }
+
+ nread = fread (buffer, 1, sizeof buffer - 1, fp);
+ fclose (fp);
+ if (!nread)
+ {
+ dbus_set_error (error, DBUS_ERROR_FILE_NOT_FOUND, "Could not read nonce from file %s", _dbus_string_get_const_data (fname));
+ return FALSE;
+ }
+
+ if (!_dbus_string_append_len (nonce, buffer, sizeof buffer - 1 ))
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+DBusSocket
+_dbus_accept_with_noncefile (DBusSocket listen_fd, const DBusNonceFile *noncefile)
+{
+ DBusSocket fd = _dbus_socket_get_invalid ();
+ DBusString nonce;
+
+ _dbus_assert (noncefile != NULL);
+
+ /* Make it valid to "free" this even if _dbus_string_init() runs
+ * out of memory: see comment in do_check_nonce() */
+ _dbus_string_init_const (&nonce, "");
+
+ if (!_dbus_string_init (&nonce))
+ goto out;
+
+ //PENDING(kdab): set better errors
+ if (_dbus_read_nonce (_dbus_noncefile_get_path(noncefile), &nonce, NULL) != TRUE)
+ goto out;
+
+ fd = _dbus_accept (listen_fd);
+
+ if (!_dbus_socket_is_valid (fd))
+ goto out;
+
+ if (do_check_nonce(fd, &nonce, NULL) != TRUE) {
+ _dbus_verbose ("nonce check failed. Closing socket.\n");
+ _dbus_close_socket (&fd, NULL);
+ goto out;
+ }
+
+out:
+ _dbus_string_free (&nonce);
+ return fd;
+}
+
+static dbus_bool_t
+generate_and_write_nonce (const DBusString *filename, DBusError *error)
+{
+ DBusString nonce;
+ dbus_bool_t ret;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ if (!_dbus_string_init (&nonce))
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ return FALSE;
+ }
+
+ if (!_dbus_generate_random_bytes (&nonce, 16, error))
+ {
+ _dbus_string_free (&nonce);
+ return FALSE;
+ }
+
+ ret = _dbus_string_save_to_file (&nonce, filename, FALSE, error);
+
+ _dbus_string_free (&nonce);
+
+ return ret;
+}
+
+/**
+ * sends the nonce over a given socket. Blocks while doing so.
+ *
+ * @param fd the file descriptor to write the nonce data to (usually a socket)
+ * @param noncefile the noncefile location to read the nonce from
+ * @param error contains error details if FALSE is returned
+ * @return TRUE iff the nonce was successfully sent. Note that this does not
+ * indicate whether the server accepted the nonce.
+ */
+dbus_bool_t
+_dbus_send_nonce (DBusSocket fd,
+ const DBusString *noncefile,
+ DBusError *error)
+{
+ dbus_bool_t read_result;
+ int send_result;
+ DBusString nonce;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ if (_dbus_string_get_length (noncefile) == 0)
+ return FALSE;
+
+ if (!_dbus_string_init (&nonce))
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ return FALSE;
+ }
+
+ read_result = _dbus_read_nonce (noncefile, &nonce, error);
+ if (!read_result)
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ _dbus_string_free (&nonce);
+ return FALSE;
+ }
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ send_result = _dbus_write_socket (fd, &nonce, 0, _dbus_string_get_length (&nonce));
+
+ _dbus_string_free (&nonce);
+
+ if (send_result == -1)
+ {
+ dbus_set_error (error,
+ _dbus_error_from_system_errno (),
+ "Failed to send nonce (fd=%" DBUS_SOCKET_FORMAT "): %s",
+ _dbus_socket_printable (fd),
+ _dbus_strerror_from_errno ());
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static dbus_bool_t
+do_noncefile_create (DBusNonceFile **noncefile_out,
+ DBusError *error,
+ dbus_bool_t use_subdir)
+{
+ DBusNonceFile *noncefile = NULL;
+ DBusString randomStr;
+ const char *tmp;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ _dbus_assert (noncefile_out != NULL);
+ _dbus_assert (*noncefile_out == NULL);
+
+ noncefile = dbus_new0 (DBusNonceFile, 1);
+ if (noncefile == NULL)
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ return FALSE;
+ }
+
+ /* Make it valid to "free" these even if _dbus_string_init() runs
+ * out of memory: see comment in do_check_nonce() */
+ _dbus_string_init_const (&randomStr, "");
+ _dbus_string_init_const (&noncefile->dir, "");
+ _dbus_string_init_const (&noncefile->path, "");
+
+ if (!_dbus_string_init (&randomStr))
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ goto on_error;
+ }
+
+ if (!_dbus_generate_random_ascii (&randomStr, 8, error))
+ {
+ goto on_error;
+ }
+
+ tmp = _dbus_get_tmpdir ();
+
+ if (!_dbus_string_init (&noncefile->dir)
+ || tmp == NULL
+ || !_dbus_string_append (&noncefile->dir, tmp))
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ goto on_error;
+ }
+ if (use_subdir)
+ {
+ if (!_dbus_string_append (&noncefile->dir, "/dbus_nonce-")
+ || !_dbus_string_append (&noncefile->dir, _dbus_string_get_const_data (&randomStr)) )
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ goto on_error;
+ }
+ if (!_dbus_string_init (&noncefile->path)
+ || !_dbus_string_copy (&noncefile->dir, 0, &noncefile->path, 0)
+ || !_dbus_string_append (&noncefile->path, "/nonce"))
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ goto on_error;
+ }
+ if (!_dbus_create_directory (&noncefile->dir, error))
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ goto on_error;
+ }
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ }
+ else
+ {
+ if (!_dbus_string_init (&noncefile->path)
+ || !_dbus_string_copy (&noncefile->dir, 0, &noncefile->path, 0)
+ || !_dbus_string_append (&noncefile->path, "/dbus_nonce-")
+ || !_dbus_string_append (&noncefile->path, _dbus_string_get_const_data (&randomStr)))
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ goto on_error;
+ }
+
+ }
+
+ if (!generate_and_write_nonce (&noncefile->path, error))
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ if (use_subdir)
+ _dbus_delete_directory (&noncefile->dir, NULL); //we ignore possible errors deleting the dir and return the write error instead
+ goto on_error;
+ }
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ *noncefile_out = noncefile;
+ _dbus_string_free (&randomStr);
+
+ return TRUE;
+ on_error:
+ if (use_subdir && _dbus_string_get_length (&noncefile->dir) != 0)
+ _dbus_delete_directory (&noncefile->dir, NULL);
+ _dbus_string_free (&noncefile->dir);
+ _dbus_string_free (&noncefile->path);
+ dbus_free (noncefile);
+ _dbus_string_free (&randomStr);
+ return FALSE;
+}
+
+#ifdef DBUS_WIN
+/**
+ * creates a nonce file in a user-readable location and writes a generated nonce to it
+ *
+ * @param noncefile_out returns the nonce file location
+ * @param error error details if creating the nonce file fails
+ * @return TRUE iff the nonce file was successfully created
+ */
+dbus_bool_t
+_dbus_noncefile_create (DBusNonceFile **noncefile_out,
+ DBusError *error)
+{
+ return do_noncefile_create (noncefile_out, error, /*use_subdir=*/FALSE);
+}
+
+/**
+ * deletes the noncefile and frees the DBusNonceFile object.
+ *
+ * If noncefile_location points to #NULL, nothing is freed or deleted,
+ * similar to dbus_error_free().
+ *
+ * @param noncefile_location the nonce file to delete. Contents will be freed and cleared to #NULL.
+ * @param error error details if the nonce file could not be deleted
+ * @return TRUE
+ */
+dbus_bool_t
+_dbus_noncefile_delete (DBusNonceFile **noncefile_location,
+ DBusError *error)
+{
+ DBusNonceFile *noncefile;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+ _dbus_assert (noncefile_location != NULL);
+
+ noncefile = *noncefile_location;
+ *noncefile_location = NULL;
+
+ if (noncefile == NULL)
+ {
+ /* Nothing to do */
+ return TRUE;
+ }
+
+ _dbus_delete_file (&noncefile->path, error);
+ _dbus_string_free (&noncefile->dir);
+ _dbus_string_free (&noncefile->path);
+ dbus_free (noncefile);
+ return TRUE;
+}
+
+#else
+/**
+ * creates a nonce file in a user-readable location and writes a generated nonce to it.
+ * Initializes the noncefile object.
+ *
+ * @param noncefile_out returns the nonce file location
+ * @param error error details if creating the nonce file fails
+ * @return TRUE iff the nonce file was successfully created
+ */
+dbus_bool_t
+_dbus_noncefile_create (DBusNonceFile **noncefile_out,
+ DBusError *error)
+{
+ return do_noncefile_create (noncefile_out, error, /*use_subdir=*/TRUE);
+}
+
+/**
+ * deletes the noncefile and frees the DBusNonceFile object.
+ *
+ * If noncefile_location points to #NULL, nothing is freed or deleted,
+ * similar to dbus_error_free().
+ *
+ * @param noncefile_location the nonce file to delete. Contents will be freed and cleared to #NULL.
+ * @param error error details if the nonce file could not be deleted
+ * @return TRUE
+ */
+dbus_bool_t
+_dbus_noncefile_delete (DBusNonceFile **noncefile_location,
+ DBusError *error)
+{
+ DBusNonceFile *noncefile;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+ _dbus_assert (noncefile_location != NULL);
+
+ noncefile = *noncefile_location;
+ *noncefile_location = NULL;
+
+ if (noncefile == NULL)
+ {
+ /* Nothing to do */
+ return TRUE;
+ }
+
+ _dbus_delete_directory (&noncefile->dir, error);
+ _dbus_string_free (&noncefile->dir);
+ _dbus_string_free (&noncefile->path);
+ dbus_free (noncefile);
+ return TRUE;
+}
+#endif
+
+
+/**
+ * returns the absolute file path of the nonce file
+ *
+ * @param noncefile an initialized noncefile object
+ * @return the absolute path of the nonce file
+ */
+const DBusString*
+_dbus_noncefile_get_path (const DBusNonceFile *noncefile)
+{
+ _dbus_assert (noncefile);
+ return &noncefile->path;
+}
+
+/**
+ * reads data from a file descriptor and checks if the received data matches
+ * the data in the given noncefile.
+ *
+ * @param fd the file descriptor to read the nonce from
+ * @param noncefile the nonce file to check the received data against
+ * @param error error details on fail
+ * @return TRUE iff a nonce could be successfully read from the file descriptor
+ * and matches the nonce from the given nonce file
+ */
+dbus_bool_t
+_dbus_noncefile_check_nonce (DBusSocket fd,
+ const DBusNonceFile *noncefile,
+ DBusError* error)
+{
+ return do_check_nonce (fd, _dbus_noncefile_get_path (noncefile), error);
+}
+
+
+/** @} end of nonce */
diff --git a/src/3rdparty/libdbus/dbus/dbus-nonce.h b/src/3rdparty/libdbus/dbus/dbus-nonce.h
new file mode 100644
index 00000000..d26dc397
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-nonce.h
@@ -0,0 +1,69 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-nonce.h Nonce handling functions used by nonce-tcp (internal to D-Bus implementation)
+ *
+ * Copyright (C) 2009 Klaralvdalens Datakonsult AB, a KDAB Group company, info@kdab.net
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#ifndef DBUS_NONCE_H
+#define DBUS_NONCE_H
+
+#include <dbus/dbus-macros.h>
+#include <dbus/dbus-types.h>
+#include <dbus/dbus-errors.h>
+#include <dbus/dbus-string.h>
+#include <dbus/dbus-sysdeps.h>
+
+DBUS_BEGIN_DECLS
+
+typedef struct DBusNonceFile DBusNonceFile;
+
+// server
+
+dbus_bool_t _dbus_noncefile_create (DBusNonceFile **noncefile_out,
+ DBusError *error);
+
+dbus_bool_t _dbus_noncefile_delete (DBusNonceFile **noncefile_location,
+ DBusError *error);
+
+dbus_bool_t _dbus_noncefile_check_nonce (DBusSocket fd,
+ const DBusNonceFile *noncefile,
+ DBusError *error);
+
+const DBusString* _dbus_noncefile_get_path (const DBusNonceFile *noncefile);
+
+DBusSocket _dbus_accept_with_noncefile(DBusSocket listen_fd,
+ const DBusNonceFile *noncefile);
+
+// shared
+
+dbus_bool_t _dbus_read_nonce (const DBusString *fname,
+ DBusString *nonce,
+ DBusError *error);
+
+// client
+
+dbus_bool_t _dbus_send_nonce (DBusSocket fd,
+ const DBusString *noncefile,
+ DBusError *error);
+
+DBUS_END_DECLS
+
+#endif /* DBUS_NONCE_H */
diff --git a/src/3rdparty/libdbus/dbus/dbus-object-tree.c b/src/3rdparty/libdbus/dbus/dbus-object-tree.c
new file mode 100644
index 00000000..bb7d5479
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-object-tree.c
@@ -0,0 +1,2333 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-object-tree.c DBusObjectTree (internals of DBusConnection)
+ *
+ * Copyright (C) 2003, 2005 Red Hat Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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-object-tree.h"
+#include "dbus-connection-internal.h"
+#include "dbus-internals.h"
+#include "dbus-hash.h"
+#include "dbus-protocol.h"
+#include "dbus-string.h"
+#include <dbus/dbus-test-tap.h>
+#include <string.h>
+#include <stdlib.h>
+
+/**
+ * @defgroup DBusObjectTree A hierarchy of objects with container-contained relationship
+ * @ingroup DBusInternals
+ * @brief DBusObjectTree is used by DBusConnection to track the object tree
+ *
+ * Types and functions related to DBusObjectTree. These
+ * are all library-internal.
+ *
+ * @{
+ */
+
+/** Subnode of the object hierarchy */
+typedef struct DBusObjectSubtree DBusObjectSubtree;
+
+static DBusObjectSubtree* _dbus_object_subtree_new (const char *name,
+ const DBusObjectPathVTable *vtable,
+ void *user_data);
+static DBusObjectSubtree* _dbus_object_subtree_ref (DBusObjectSubtree *subtree);
+static void _dbus_object_subtree_unref (DBusObjectSubtree *subtree);
+
+/**
+ * Internals of DBusObjectTree
+ */
+struct DBusObjectTree
+{
+ int refcount; /**< Reference count */
+ DBusConnection *connection; /**< Connection this tree belongs to */
+
+ DBusObjectSubtree *root; /**< Root of the tree ("/" node) */
+};
+
+/**
+ * Struct representing a single registered subtree handler, or node
+ * that's a parent of a registered subtree handler. If
+ * message_function != NULL there's actually a handler at this node.
+ */
+struct DBusObjectSubtree
+{
+ DBusAtomic refcount; /**< Reference count */
+ DBusObjectSubtree *parent; /**< Parent node */
+ DBusObjectPathUnregisterFunction unregister_function; /**< Function to call on unregister */
+ DBusObjectPathMessageFunction message_function; /**< Function to handle messages */
+ void *user_data; /**< Data for functions */
+ DBusObjectSubtree **subtrees; /**< Child nodes */
+ int n_subtrees; /**< Number of child nodes */
+ int max_subtrees; /**< Number of allocated entries in subtrees */
+ unsigned int invoke_as_fallback : 1; /**< Whether to invoke message_function when child nodes don't handle the message */
+ char name[1]; /**< Allocated as large as necessary */
+};
+
+/**
+ * Creates a new object tree, representing a mapping from paths
+ * to handler vtables.
+ *
+ * @param connection the connection this tree belongs to
+ * @returns the new tree or #NULL if no memory
+ */
+DBusObjectTree*
+_dbus_object_tree_new (DBusConnection *connection)
+{
+ DBusObjectTree *tree;
+
+ /* the connection passed in here isn't fully constructed,
+ * so don't do anything more than store a pointer to
+ * it
+ */
+
+ tree = dbus_new0 (DBusObjectTree, 1);
+ if (tree == NULL)
+ goto oom;
+
+ tree->refcount = 1;
+ tree->connection = connection;
+ tree->root = _dbus_object_subtree_new ("/", NULL, NULL);
+ if (tree->root == NULL)
+ goto oom;
+ tree->root->invoke_as_fallback = TRUE;
+
+ return tree;
+
+ oom:
+ if (tree)
+ {
+ dbus_free (tree);
+ }
+
+ return NULL;
+}
+
+/**
+ * Increment the reference count
+ * @param tree the object tree
+ * @returns the object tree
+ */
+DBusObjectTree *
+_dbus_object_tree_ref (DBusObjectTree *tree)
+{
+ _dbus_assert (tree->refcount > 0);
+
+ tree->refcount += 1;
+
+ return tree;
+}
+
+/**
+ * Decrement the reference count
+ * @param tree the object tree
+ */
+void
+_dbus_object_tree_unref (DBusObjectTree *tree)
+{
+ _dbus_assert (tree->refcount > 0);
+
+ tree->refcount -= 1;
+
+ if (tree->refcount == 0)
+ {
+ _dbus_object_tree_free_all_unlocked (tree);
+
+ dbus_free (tree);
+ }
+}
+
+/** Set to 1 to get a bunch of debug spew about finding the
+ * subtree nodes
+ */
+#define VERBOSE_FIND 0
+
+static DBusObjectSubtree*
+find_subtree_recurse (DBusObjectSubtree *subtree,
+ const char **path,
+ dbus_bool_t create_if_not_found,
+ int *index_in_parent,
+ dbus_bool_t *exact_match)
+{
+ int i, j;
+ dbus_bool_t return_deepest_match;
+
+ return_deepest_match = exact_match != NULL;
+
+ _dbus_assert (!(return_deepest_match && create_if_not_found));
+
+ if (path[0] == NULL)
+ {
+#if VERBOSE_FIND
+ _dbus_verbose (" path exhausted, returning %s\n",
+ subtree->name);
+#endif
+ if (exact_match != NULL)
+ *exact_match = TRUE;
+ return subtree;
+ }
+
+#if VERBOSE_FIND
+ _dbus_verbose (" searching children of %s for %s\n",
+ subtree->name, path[0]);
+#endif
+
+ i = 0;
+ j = subtree->n_subtrees;
+ while (i < j)
+ {
+ int k, v;
+
+ k = (i + j) / 2;
+ v = strcmp (path[0], subtree->subtrees[k]->name);
+
+#if VERBOSE_FIND
+ _dbus_verbose (" %s cmp %s = %d\n",
+ path[0], subtree->subtrees[k]->name,
+ v);
+#endif
+
+ if (v == 0)
+ {
+ if (index_in_parent)
+ {
+#if VERBOSE_FIND
+ _dbus_verbose (" storing parent index %d\n", k);
+#endif
+ *index_in_parent = k;
+ }
+
+ if (return_deepest_match)
+ {
+ DBusObjectSubtree *next;
+
+ next = find_subtree_recurse (subtree->subtrees[k],
+ &path[1], create_if_not_found,
+ index_in_parent, exact_match);
+ if (next == NULL &&
+ subtree->invoke_as_fallback)
+ {
+#if VERBOSE_FIND
+ _dbus_verbose (" no deeper match found, returning %s\n",
+ subtree->name);
+#endif
+ if (exact_match != NULL)
+ *exact_match = FALSE;
+ return subtree;
+ }
+ else
+ return next;
+ }
+ else
+ return find_subtree_recurse (subtree->subtrees[k],
+ &path[1], create_if_not_found,
+ index_in_parent, exact_match);
+ }
+ else if (v < 0)
+ {
+ j = k;
+ }
+ else
+ {
+ i = k + 1;
+ }
+ }
+
+#if VERBOSE_FIND
+ _dbus_verbose (" no match found, current tree %s, create_if_not_found = %d\n",
+ subtree->name, create_if_not_found);
+#endif
+
+ if (create_if_not_found)
+ {
+ DBusObjectSubtree* child;
+ int child_pos, new_n_subtrees;
+
+#if VERBOSE_FIND
+ _dbus_verbose (" creating subtree %s\n",
+ path[0]);
+#endif
+
+ child = _dbus_object_subtree_new (path[0],
+ NULL, NULL);
+ if (child == NULL)
+ return NULL;
+
+ new_n_subtrees = subtree->n_subtrees + 1;
+ if (new_n_subtrees > subtree->max_subtrees)
+ {
+ int new_max_subtrees;
+ DBusObjectSubtree **new_subtrees;
+
+ new_max_subtrees = subtree->max_subtrees == 0 ? 1 : 2 * subtree->max_subtrees;
+ new_subtrees = dbus_realloc (subtree->subtrees,
+ new_max_subtrees * sizeof (DBusObjectSubtree*));
+ if (new_subtrees == NULL)
+ {
+ _dbus_object_subtree_unref (child);
+ return NULL;
+ }
+ subtree->subtrees = new_subtrees;
+ subtree->max_subtrees = new_max_subtrees;
+ }
+
+ /* The binary search failed, so i == j points to the
+ place the child should be inserted. */
+ child_pos = i;
+ _dbus_assert (child_pos < new_n_subtrees &&
+ new_n_subtrees <= subtree->max_subtrees);
+ if (child_pos + 1 < new_n_subtrees)
+ {
+ memmove (&subtree->subtrees[child_pos+1],
+ &subtree->subtrees[child_pos],
+ (new_n_subtrees - child_pos - 1) *
+ sizeof subtree->subtrees[0]);
+ }
+ subtree->subtrees[child_pos] = child;
+
+ if (index_in_parent)
+ *index_in_parent = child_pos;
+ subtree->n_subtrees = new_n_subtrees;
+ child->parent = subtree;
+
+ return find_subtree_recurse (child,
+ &path[1], create_if_not_found,
+ index_in_parent, exact_match);
+ }
+ else
+ {
+ if (exact_match != NULL)
+ *exact_match = FALSE;
+ return (return_deepest_match && subtree->invoke_as_fallback) ? subtree : NULL;
+ }
+}
+
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
+static DBusObjectSubtree*
+find_subtree (DBusObjectTree *tree,
+ const char **path,
+ int *index_in_parent)
+{
+ DBusObjectSubtree *subtree;
+
+#if VERBOSE_FIND
+ _dbus_verbose ("Looking for exact registered subtree\n");
+#endif
+
+ subtree = find_subtree_recurse (tree->root, path, FALSE, index_in_parent, NULL);
+
+ if (subtree && subtree->message_function == NULL)
+ return NULL;
+ else
+ return subtree;
+}
+#endif
+
+static DBusObjectSubtree*
+lookup_subtree (DBusObjectTree *tree,
+ const char **path)
+{
+#if VERBOSE_FIND
+ _dbus_verbose ("Looking for subtree\n");
+#endif
+ return find_subtree_recurse (tree->root, path, FALSE, NULL, NULL);
+}
+
+static DBusObjectSubtree*
+find_handler (DBusObjectTree *tree,
+ const char **path,
+ dbus_bool_t *exact_match)
+{
+#if VERBOSE_FIND
+ _dbus_verbose ("Looking for deepest handler\n");
+#endif
+ _dbus_assert (exact_match != NULL);
+
+ *exact_match = FALSE; /* ensure always initialized */
+
+ return find_subtree_recurse (tree->root, path, FALSE, NULL, exact_match);
+}
+
+static DBusObjectSubtree*
+ensure_subtree (DBusObjectTree *tree,
+ const char **path)
+{
+#if VERBOSE_FIND
+ _dbus_verbose ("Ensuring subtree\n");
+#endif
+ return find_subtree_recurse (tree->root, path, TRUE, NULL, NULL);
+}
+
+static char *flatten_path (const char **path);
+
+/**
+ * Registers a new subtree in the global object tree.
+ *
+ * @param tree the global object tree
+ * @param fallback #TRUE to handle messages to children of this path
+ * @param path NULL-terminated array of path elements giving path to subtree
+ * @param vtable the vtable used to traverse this subtree
+ * @param user_data user data to pass to methods in the vtable
+ * @param error address where an error can be returned
+ * @returns #FALSE if an error (#DBUS_ERROR_NO_MEMORY or
+ * #DBUS_ERROR_OBJECT_PATH_IN_USE) is reported
+ */
+dbus_bool_t
+_dbus_object_tree_register (DBusObjectTree *tree,
+ dbus_bool_t fallback,
+ const char **path,
+ const DBusObjectPathVTable *vtable,
+ void *user_data,
+ DBusError *error)
+{
+ DBusObjectSubtree *subtree;
+
+ _dbus_assert (tree != NULL);
+ _dbus_assert (vtable->message_function != NULL);
+ _dbus_assert (path != NULL);
+
+ subtree = ensure_subtree (tree, path);
+ if (subtree == NULL)
+ {
+ _DBUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ if (subtree->message_function != NULL)
+ {
+ if (error != NULL)
+ {
+ char *complete_path = flatten_path (path);
+
+ dbus_set_error (error, DBUS_ERROR_OBJECT_PATH_IN_USE,
+ "A handler is already registered for %s",
+ complete_path ? complete_path
+ : "(cannot represent path: out of memory!)");
+
+ dbus_free (complete_path);
+ }
+
+ return FALSE;
+ }
+
+ subtree->message_function = vtable->message_function;
+ subtree->unregister_function = vtable->unregister_function;
+ subtree->user_data = user_data;
+ subtree->invoke_as_fallback = fallback != FALSE;
+
+ return TRUE;
+}
+
+/**
+ * Attempts to unregister the given subtree. If the subtree is registered,
+ * stores its unregister function and user data for later use and returns
+ * #TRUE. If subtree is not registered, simply returns #FALSE. Does not free
+ * subtree or remove it from the object tree.
+ *
+ * @param subtree the subtree to unregister
+ * @param unregister_function_out stores subtree's unregister_function
+ * @param user_data_out stores subtree's user_data
+ * @return #FALSE if the subtree was not registered, #TRUE on success
+ */
+static dbus_bool_t
+unregister_subtree (DBusObjectSubtree *subtree,
+ DBusObjectPathUnregisterFunction *unregister_function_out,
+ void **user_data_out)
+{
+ _dbus_assert (subtree != NULL);
+ _dbus_assert (unregister_function_out != NULL);
+ _dbus_assert (user_data_out != NULL);
+
+ /* Confirm subtree is registered */
+ if (subtree->message_function != NULL)
+ {
+ subtree->message_function = NULL;
+
+ *unregister_function_out = subtree->unregister_function;
+ *user_data_out = subtree->user_data;
+
+ subtree->unregister_function = NULL;
+ subtree->user_data = NULL;
+
+ return TRUE;
+ }
+ else
+ {
+ /* Assert that this unregistered subtree is either the root node or has
+ children, otherwise we have a dangling path which should never
+ happen */
+ _dbus_assert (subtree->parent == NULL || subtree->n_subtrees > 0);
+
+ /* The subtree is not registered */
+ return FALSE;
+ }
+}
+
+/**
+ * Attempts to remove a child subtree from its parent. If removal is
+ * successful, also frees the child. Returns #TRUE on success, #FALSE
+ * otherwise. A #FALSE return value tells unregister_and_free_path_recurse to
+ * stop attempting to remove ancestors, i.e., that no ancestors of the
+ * specified child are eligible for removal.
+ *
+ * @param parent parent from which to remove child
+ * @param child_index parent->subtrees index of child to remove
+ * @return #TRUE if removal and free succeed, #FALSE otherwise
+ */
+static dbus_bool_t
+attempt_child_removal (DBusObjectSubtree *parent,
+ int child_index)
+{
+ /* Candidate for removal */
+ DBusObjectSubtree* candidate;
+
+ _dbus_assert (parent != NULL);
+ _dbus_assert (child_index >= 0 && child_index < parent->n_subtrees);
+
+ candidate = parent->subtrees[child_index];
+ _dbus_assert (candidate != NULL);
+
+ if (candidate->n_subtrees == 0 && candidate->message_function == NULL)
+ {
+ /* The candidate node is childless and is not a registered
+ path, so... */
+
+ /* ... remove it from its parent... */
+ /* Assumes a 0-byte memmove is OK */
+ memmove (&parent->subtrees[child_index],
+ &parent->subtrees[child_index + 1],
+ (parent->n_subtrees - child_index - 1)
+ * sizeof (parent->subtrees[0]));
+ parent->n_subtrees -= 1;
+
+ /* ... and free it */
+ candidate->parent = NULL;
+ _dbus_object_subtree_unref (candidate);
+
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/**
+ * Searches the object tree for a registered subtree node at the given path.
+ * If a registered node is found, it is removed from the tree and freed, and
+ * TRUE is returned. If a registered subtree node is not found at the given
+ * path, the tree is not modified and FALSE is returned.
+ *
+ * The found node's unregister_function and user_data are returned in the
+ * corresponding _out arguments. The caller should define these variables and
+ * pass their addresses as arguments.
+ *
+ * Likewise, the caller should define and set to TRUE a boolean variable, then
+ * pass its address as the continue_removal_attempts argument.
+ *
+ * Once a matching registered node is found, removed and freed, the recursive
+ * return path is traversed. Along the way, eligible ancestor nodes are
+ * removed and freed. An ancestor node is eligible for removal if and only if
+ * 1) it has no children, i.e., it has become childless and 2) it is not itself
+ * a registered handler.
+ *
+ * For example, suppose /A/B and /A/C are registered paths, and that these are
+ * the only paths in the tree. If B is removed and freed, C is still reachable
+ * through A, so A cannot be removed and freed. If C is subsequently removed
+ * and freed, then A becomes a childless node and it becomes eligible for
+ * removal, and will be removed and freed.
+ *
+ * Similarly, suppose /A is a registered path, and /A/B is also a registered
+ * path, and that these are the only paths in the tree. If B is removed and
+ * freed, then even though A has become childless, it can't be freed because it
+ * refers to a path that is still registered.
+ *
+ * @param subtree subtree from which to start the search, root for initial call
+ * @param path path to subtree (same as _dbus_object_tree_unregister_and_unlock)
+ * @param continue_removal_attempts pointer to a bool, #TRUE for initial call
+ * @param unregister_function_out returns the found node's unregister_function
+ * @param user_data_out returns the found node's user_data
+ * @returns #TRUE if a registered node was found at path, #FALSE otherwise
+ */
+static dbus_bool_t
+unregister_and_free_path_recurse
+(DBusObjectSubtree *subtree,
+ const char **path,
+ dbus_bool_t *continue_removal_attempts,
+ DBusObjectPathUnregisterFunction *unregister_function_out,
+ void **user_data_out)
+{
+ int i, j;
+
+ _dbus_assert (continue_removal_attempts != NULL);
+ _dbus_assert (*continue_removal_attempts);
+ _dbus_assert (unregister_function_out != NULL);
+ _dbus_assert (user_data_out != NULL);
+
+ if (path[0] == NULL)
+ return unregister_subtree (subtree, unregister_function_out, user_data_out);
+
+ i = 0;
+ j = subtree->n_subtrees;
+ while (i < j)
+ {
+ int k, v;
+
+ k = (i + j) / 2;
+ v = strcmp (path[0], subtree->subtrees[k]->name);
+
+ if (v == 0)
+ {
+ dbus_bool_t freed;
+ freed = unregister_and_free_path_recurse (subtree->subtrees[k],
+ &path[1],
+ continue_removal_attempts,
+ unregister_function_out,
+ user_data_out);
+ if (freed && *continue_removal_attempts)
+ *continue_removal_attempts = attempt_child_removal (subtree, k);
+ return freed;
+ }
+ else if (v < 0)
+ {
+ j = k;
+ }
+ else
+ {
+ i = k + 1;
+ }
+ }
+ return FALSE;
+}
+
+/**
+ * Unregisters an object subtree that was registered with the
+ * same path.
+ *
+ * @param tree the global object tree
+ * @param path path to the subtree (same as the one passed to _dbus_object_tree_register())
+ */
+void
+_dbus_object_tree_unregister_and_unlock (DBusObjectTree *tree,
+ const char **path)
+{
+ dbus_bool_t found_subtree;
+ dbus_bool_t continue_removal_attempts;
+ DBusObjectPathUnregisterFunction unregister_function;
+ void *user_data;
+ DBusConnection *connection;
+
+ _dbus_assert (tree != NULL);
+ _dbus_assert (path != NULL);
+
+ continue_removal_attempts = TRUE;
+ unregister_function = NULL;
+ user_data = NULL;
+
+ found_subtree = unregister_and_free_path_recurse (tree->root,
+ path,
+ &continue_removal_attempts,
+ &unregister_function,
+ &user_data);
+
+#ifndef DBUS_DISABLE_CHECKS
+ if (found_subtree == FALSE)
+ {
+ _dbus_warn ("Attempted to unregister path (path[0] = %s path[1] = %s) which isn't registered",
+ path[0] ? path[0] : "null",
+ (path[0] && path[1]) ? path[1] : "null");
+ goto unlock;
+ }
+#else
+ _dbus_assert (found_subtree == TRUE);
+#endif
+
+unlock:
+ connection = tree->connection;
+
+ /* Unlock and call application code */
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
+ if (connection)
+#endif
+ {
+ _dbus_connection_ref_unlocked (connection);
+ _dbus_verbose ("unlock\n");
+ _dbus_connection_unlock (connection);
+ }
+
+ if (unregister_function)
+ (* unregister_function) (connection, user_data);
+
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
+ if (connection)
+#endif
+ dbus_connection_unref (connection);
+}
+
+static void
+free_subtree_recurse (DBusConnection *connection,
+ DBusObjectSubtree *subtree)
+{
+ /* Delete them from the end, for slightly
+ * more robustness against odd reentrancy.
+ */
+ while (subtree->n_subtrees > 0)
+ {
+ DBusObjectSubtree *child;
+
+ child = subtree->subtrees[subtree->n_subtrees - 1];
+ subtree->subtrees[subtree->n_subtrees - 1] = NULL;
+ subtree->n_subtrees -= 1;
+ child->parent = NULL;
+
+ free_subtree_recurse (connection, child);
+ }
+
+ /* Call application code */
+ if (subtree->unregister_function)
+ (* subtree->unregister_function) (connection,
+ subtree->user_data);
+
+ subtree->message_function = NULL;
+ subtree->unregister_function = NULL;
+ subtree->user_data = NULL;
+
+ /* Now free ourselves */
+ _dbus_object_subtree_unref (subtree);
+}
+
+/**
+ * Free all the handlers in the tree. Lock on tree's connection
+ * must not be held.
+ *
+ * @param tree the object tree
+ */
+void
+_dbus_object_tree_free_all_unlocked (DBusObjectTree *tree)
+{
+ if (tree->root)
+ free_subtree_recurse (tree->connection,
+ tree->root);
+ tree->root = NULL;
+}
+
+static dbus_bool_t
+_dbus_object_tree_list_registered_unlocked (DBusObjectTree *tree,
+ const char **parent_path,
+ char ***child_entries)
+{
+ DBusObjectSubtree *subtree;
+ char **retval;
+
+ _dbus_assert (parent_path != NULL);
+ _dbus_assert (child_entries != NULL);
+
+ *child_entries = NULL;
+
+ subtree = lookup_subtree (tree, parent_path);
+ if (subtree == NULL)
+ {
+ retval = dbus_new0 (char *, 1);
+ }
+ else
+ {
+ int i;
+ retval = dbus_new0 (char*, subtree->n_subtrees + 1);
+ if (retval == NULL)
+ goto out;
+ i = 0;
+ while (i < subtree->n_subtrees)
+ {
+ retval[i] = _dbus_strdup (subtree->subtrees[i]->name);
+ if (retval[i] == NULL)
+ {
+ dbus_free_string_array (retval);
+ retval = NULL;
+ goto out;
+ }
+ ++i;
+ }
+ }
+
+ out:
+
+ *child_entries = retval;
+ return retval != NULL;
+}
+
+static DBusHandlerResult
+handle_default_introspect_and_unlock (DBusObjectTree *tree,
+ DBusMessage *message,
+ const char **path)
+{
+ DBusString xml;
+ DBusHandlerResult result;
+ char **children;
+ int i;
+ DBusMessage *reply;
+ DBusMessageIter iter;
+ const char *v_STRING;
+ dbus_bool_t already_unlocked;
+
+ /* We have the connection lock here */
+
+ already_unlocked = FALSE;
+
+ _dbus_verbose (" considering default Introspect() handler...\n");
+
+ reply = NULL;
+
+ if (!dbus_message_is_method_call (message,
+ DBUS_INTERFACE_INTROSPECTABLE,
+ "Introspect"))
+ {
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
+ if (tree->connection)
+#endif
+ {
+ _dbus_verbose ("unlock\n");
+ _dbus_connection_unlock (tree->connection);
+ }
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+
+ _dbus_verbose (" using default Introspect() handler!\n");
+
+ if (!_dbus_string_init (&xml))
+ {
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
+ if (tree->connection)
+#endif
+ {
+ _dbus_verbose ("unlock\n");
+ _dbus_connection_unlock (tree->connection);
+ }
+
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+ }
+
+ result = DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+ children = NULL;
+ if (!_dbus_object_tree_list_registered_unlocked (tree, path, &children))
+ goto out;
+
+ if (!_dbus_string_append (&xml, DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE))
+ goto out;
+
+ if (!_dbus_string_append (&xml, "<node>\n"))
+ goto out;
+
+ i = 0;
+ while (children[i] != NULL)
+ {
+ if (!_dbus_string_append_printf (&xml, " <node name=\"%s\"/>\n",
+ children[i]))
+ goto out;
+
+ ++i;
+ }
+
+ if (!_dbus_string_append (&xml, "</node>\n"))
+ goto out;
+
+ reply = dbus_message_new_method_return (message);
+ if (reply == NULL)
+ goto out;
+
+ dbus_message_iter_init_append (reply, &iter);
+ v_STRING = _dbus_string_get_const_data (&xml);
+ if (!dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &v_STRING))
+ goto out;
+
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
+ if (tree->connection)
+#endif
+ {
+ already_unlocked = TRUE;
+
+ if (!_dbus_connection_send_and_unlock (tree->connection, reply, NULL))
+ goto out;
+ }
+
+ result = DBUS_HANDLER_RESULT_HANDLED;
+
+ out:
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
+ if (tree->connection)
+#endif
+ {
+ if (!already_unlocked)
+ {
+ _dbus_verbose ("unlock\n");
+ _dbus_connection_unlock (tree->connection);
+ }
+ }
+
+ _dbus_string_free (&xml);
+ dbus_free_string_array (children);
+ if (reply)
+ dbus_message_unref (reply);
+
+ return result;
+}
+
+/**
+ * Tries to dispatch a message by directing it to handler for the
+ * object path listed in the message header, if any. Messages are
+ * dispatched first to the registered handler that matches the largest
+ * number of path elements; that is, message to /foo/bar/baz would go
+ * to the handler for /foo/bar before the one for /foo.
+ *
+ * @todo thread problems
+ *
+ * @param tree the global object tree
+ * @param message the message to dispatch
+ * @param found_object return location for the object
+ * @returns whether message was handled successfully
+ */
+DBusHandlerResult
+_dbus_object_tree_dispatch_and_unlock (DBusObjectTree *tree,
+ DBusMessage *message,
+ dbus_bool_t *found_object)
+{
+ char **path;
+ dbus_bool_t exact_match;
+ DBusList *list;
+ DBusList *link;
+ DBusHandlerResult result;
+ DBusObjectSubtree *subtree;
+
+#if 0
+ _dbus_verbose ("Dispatch of message by object path\n");
+#endif
+
+ path = NULL;
+ if (!dbus_message_get_path_decomposed (message, &path))
+ {
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
+ if (tree->connection)
+#endif
+ {
+ _dbus_verbose ("unlock\n");
+ _dbus_connection_unlock (tree->connection);
+ }
+
+ _dbus_verbose ("No memory to get decomposed path\n");
+
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+ }
+
+ if (path == NULL)
+ {
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
+ if (tree->connection)
+#endif
+ {
+ _dbus_verbose ("unlock\n");
+ _dbus_connection_unlock (tree->connection);
+ }
+
+ _dbus_verbose ("No path field in message\n");
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+
+ /* Find the deepest path that covers the path in the message */
+ subtree = find_handler (tree, (const char**) path, &exact_match);
+
+ if (found_object)
+ *found_object = !!subtree;
+
+ /* Build a list of all paths that cover the path in the message */
+
+ list = NULL;
+
+ while (subtree != NULL)
+ {
+ if (subtree->message_function != NULL && (exact_match || subtree->invoke_as_fallback))
+ {
+ _dbus_object_subtree_ref (subtree);
+
+ /* run deepest paths first */
+ if (!_dbus_list_append (&list, subtree))
+ {
+ result = DBUS_HANDLER_RESULT_NEED_MEMORY;
+ _dbus_object_subtree_unref (subtree);
+ goto free_and_return;
+ }
+ }
+
+ exact_match = FALSE;
+ subtree = subtree->parent;
+ }
+
+ _dbus_verbose ("%d handlers in the path tree for this message\n",
+ _dbus_list_get_length (&list));
+
+ /* Invoke each handler in the list */
+
+ result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ link = _dbus_list_get_first_link (&list);
+ while (link != NULL)
+ {
+ DBusList *next = _dbus_list_get_next_link (&list, link);
+ subtree = link->data;
+
+ /* message_function is NULL if we're unregistered
+ * due to reentrancy
+ */
+ if (subtree->message_function)
+ {
+ DBusObjectPathMessageFunction message_function;
+ void *user_data;
+
+ message_function = subtree->message_function;
+ user_data = subtree->user_data;
+
+#if 0
+ _dbus_verbose (" (invoking a handler)\n");
+#endif
+
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
+ if (tree->connection)
+#endif
+ {
+ _dbus_verbose ("unlock\n");
+ _dbus_connection_unlock (tree->connection);
+ }
+
+ /* FIXME you could unregister the subtree in another thread
+ * before we invoke the callback, and I can't figure out a
+ * good way to solve this.
+ */
+
+ result = (* message_function) (tree->connection,
+ message,
+ user_data);
+
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
+ if (tree->connection)
+#endif
+ _dbus_connection_lock (tree->connection);
+
+ if (result != DBUS_HANDLER_RESULT_NOT_YET_HANDLED)
+ goto free_and_return;
+ }
+
+ link = next;
+ }
+
+ free_and_return:
+
+ if (result == DBUS_HANDLER_RESULT_NOT_YET_HANDLED)
+ {
+ /* This hardcoded default handler does a minimal Introspect()
+ */
+ result = handle_default_introspect_and_unlock (tree, message,
+ (const char**) path);
+ }
+ else
+ {
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
+ if (tree->connection)
+#endif
+ {
+ _dbus_verbose ("unlock\n");
+ _dbus_connection_unlock (tree->connection);
+ }
+ }
+
+ while (list != NULL)
+ {
+ link = _dbus_list_get_first_link (&list);
+ _dbus_object_subtree_unref (link->data);
+ _dbus_list_remove_link (&list, link);
+ }
+
+ dbus_free_string_array (path);
+
+ return result;
+}
+
+/**
+ * Looks up the data passed to _dbus_object_tree_register() for a
+ * handler at the given path.
+ *
+ * @param tree the global object tree
+ * @param path NULL-terminated array of path elements giving path to subtree
+ * @returns the object's user_data or #NULL if none found
+ */
+void*
+_dbus_object_tree_get_user_data_unlocked (DBusObjectTree *tree,
+ const char **path)
+{
+ dbus_bool_t exact_match;
+ DBusObjectSubtree *subtree;
+
+ _dbus_assert (tree != NULL);
+ _dbus_assert (path != NULL);
+
+ /* Find the deepest path that covers the path in the message */
+ subtree = find_handler (tree, (const char**) path, &exact_match);
+
+ if ((subtree == NULL) || !exact_match)
+ {
+ _dbus_verbose ("No object at specified path found\n");
+ return NULL;
+ }
+
+ return subtree->user_data;
+}
+
+/**
+ * Allocates a subtree object.
+ *
+ * @param name name to duplicate.
+ * @returns newly-allocated subtree
+ */
+static DBusObjectSubtree*
+allocate_subtree_object (const char *name)
+{
+ int len;
+ DBusObjectSubtree *subtree;
+ const size_t front_padding = _DBUS_STRUCT_OFFSET (DBusObjectSubtree, name);
+
+ _dbus_assert (name != NULL);
+
+ len = strlen (name);
+
+ subtree = dbus_malloc0 (MAX (front_padding + (len + 1), sizeof (DBusObjectSubtree)));
+
+ if (subtree == NULL)
+ return NULL;
+
+ memcpy (subtree->name, name, len + 1);
+
+ return subtree;
+}
+
+static DBusObjectSubtree*
+_dbus_object_subtree_new (const char *name,
+ const DBusObjectPathVTable *vtable,
+ void *user_data)
+{
+ DBusObjectSubtree *subtree;
+
+ subtree = allocate_subtree_object (name);
+ if (subtree == NULL)
+ goto oom;
+
+ _dbus_assert (name != NULL);
+
+ subtree->parent = NULL;
+
+ if (vtable)
+ {
+ subtree->message_function = vtable->message_function;
+ subtree->unregister_function = vtable->unregister_function;
+ }
+ else
+ {
+ subtree->message_function = NULL;
+ subtree->unregister_function = NULL;
+ }
+
+ subtree->user_data = user_data;
+ _dbus_atomic_inc (&subtree->refcount);
+ subtree->subtrees = NULL;
+ subtree->n_subtrees = 0;
+ subtree->max_subtrees = 0;
+ subtree->invoke_as_fallback = FALSE;
+
+ return subtree;
+
+ oom:
+ return NULL;
+}
+
+static DBusObjectSubtree *
+_dbus_object_subtree_ref (DBusObjectSubtree *subtree)
+{
+#ifdef DBUS_DISABLE_ASSERT
+ _dbus_atomic_inc (&subtree->refcount);
+#else
+ dbus_int32_t old_value;
+
+ old_value = _dbus_atomic_inc (&subtree->refcount);
+ _dbus_assert (old_value > 0);
+#endif
+
+ return subtree;
+}
+
+static void
+_dbus_object_subtree_unref (DBusObjectSubtree *subtree)
+{
+ dbus_int32_t old_value;
+
+ old_value = _dbus_atomic_dec (&subtree->refcount);
+ _dbus_assert (old_value > 0);
+
+ if (old_value == 1)
+ {
+ _dbus_assert (subtree->unregister_function == NULL);
+ _dbus_assert (subtree->message_function == NULL);
+
+ dbus_free (subtree->subtrees);
+ dbus_free (subtree);
+ }
+}
+
+/**
+ * Lists the registered fallback handlers and object path handlers at
+ * the given parent_path. The returned array should be freed with
+ * dbus_free_string_array().
+ *
+ * @param tree the object tree
+ * @param parent_path the path to list the child handlers of
+ * @param child_entries returns #NULL-terminated array of children
+ * @returns #FALSE if no memory to allocate the child entries
+ */
+dbus_bool_t
+_dbus_object_tree_list_registered_and_unlock (DBusObjectTree *tree,
+ const char **parent_path,
+ char ***child_entries)
+{
+ dbus_bool_t result;
+
+ result = _dbus_object_tree_list_registered_unlocked (tree,
+ parent_path,
+ child_entries);
+
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
+ if (tree->connection)
+#endif
+ {
+ _dbus_verbose ("unlock\n");
+ _dbus_connection_unlock (tree->connection);
+ }
+
+ return result;
+}
+
+
+/** Set to 1 to get a bunch of spew about disassembling the path string */
+#define VERBOSE_DECOMPOSE 0
+
+/**
+ * Decompose an object path. A path of just "/" is
+ * represented as an empty vector of strings.
+ * The path need not be nul terminated.
+ *
+ * @param data the path data
+ * @param len the length of the path string
+ * @param path address to store new object path
+ * @param path_len length of stored path
+ */
+dbus_bool_t
+_dbus_decompose_path (const char* data,
+ int len,
+ char ***path,
+ int *path_len)
+{
+ char **retval;
+ int n_components;
+ int i, j, comp;
+
+ _dbus_assert (data != NULL);
+ _dbus_assert (path != NULL);
+
+#if VERBOSE_DECOMPOSE
+ _dbus_verbose ("Decomposing path \"%s\"\n",
+ data);
+#endif
+
+ n_components = 0;
+ if (len > 1) /* if path is not just "/" */
+ {
+ i = 0;
+ while (i < len)
+ {
+ _dbus_assert (data[i] != '\0');
+ if (data[i] == '/')
+ n_components += 1;
+ ++i;
+ }
+ }
+
+ retval = dbus_new0 (char*, n_components + 1);
+
+ if (retval == NULL)
+ return FALSE;
+
+ comp = 0;
+ if (n_components == 0)
+ i = 1;
+ else
+ i = 0;
+ while (comp < n_components)
+ {
+ _dbus_assert (i < len);
+
+ if (data[i] == '/')
+ ++i;
+ j = i;
+
+ while (j < len && data[j] != '/')
+ ++j;
+
+ /* Now [i, j) is the path component */
+ _dbus_assert (i < j);
+ _dbus_assert (data[i] != '/');
+ _dbus_assert (j == len || data[j] == '/');
+
+#if VERBOSE_DECOMPOSE
+ _dbus_verbose (" (component in [%d,%d))\n",
+ i, j);
+#endif
+
+ retval[comp] = _dbus_memdup (&data[i], j - i + 1);
+ if (retval[comp] == NULL)
+ {
+ dbus_free_string_array (retval);
+ return FALSE;
+ }
+ retval[comp][j-i] = '\0';
+#if VERBOSE_DECOMPOSE
+ _dbus_verbose (" (component %d = \"%s\")\n",
+ comp, retval[comp]);
+#endif
+
+ ++comp;
+ i = j;
+ }
+ _dbus_assert (i == len);
+
+ *path = retval;
+ if (path_len)
+ *path_len = n_components;
+
+ return TRUE;
+}
+
+/** @} */
+
+static char*
+flatten_path (const char **path)
+{
+ DBusString str;
+ char *s;
+
+ if (!_dbus_string_init (&str))
+ return NULL;
+
+ if (path[0] == NULL)
+ {
+ if (!_dbus_string_append_byte (&str, '/'))
+ goto nomem;
+ }
+ else
+ {
+ int i;
+
+ i = 0;
+ while (path[i])
+ {
+ if (!_dbus_string_append_byte (&str, '/'))
+ goto nomem;
+
+ if (!_dbus_string_append (&str, path[i]))
+ goto nomem;
+
+ ++i;
+ }
+ }
+
+ if (!_dbus_string_steal_data (&str, &s))
+ goto nomem;
+
+ _dbus_string_free (&str);
+
+ return s;
+
+ nomem:
+ _dbus_string_free (&str);
+ return NULL;
+}
+
+
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
+
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+
+#include "dbus-test.h"
+#include <stdio.h>
+
+typedef enum
+{
+ STR_EQUAL,
+ STR_PREFIX,
+ STR_DIFFERENT
+} StrComparison;
+
+/* Returns TRUE if container is a parent of child
+ */
+static StrComparison
+path_contains (const char **container,
+ const char **child)
+{
+ int i;
+
+ i = 0;
+ while (child[i] != NULL)
+ {
+ int v;
+
+ if (container[i] == NULL)
+ return STR_PREFIX; /* container ran out, child continues;
+ * thus the container is a parent of the
+ * child.
+ */
+
+ _dbus_assert (container[i] != NULL);
+ _dbus_assert (child[i] != NULL);
+
+ v = strcmp (container[i], child[i]);
+
+ if (v != 0)
+ return STR_DIFFERENT; /* they overlap until here and then are different,
+ * not overlapping
+ */
+
+ ++i;
+ }
+
+ /* Child ran out; if container also did, they are equal;
+ * otherwise, the child is a parent of the container.
+ */
+ if (container[i] == NULL)
+ return STR_EQUAL;
+ else
+ return STR_DIFFERENT;
+}
+
+#if 0
+static void
+spew_subtree_recurse (DBusObjectSubtree *subtree,
+ int indent)
+{
+ int i;
+
+ i = 0;
+ while (i < indent)
+ {
+ _dbus_verbose (" ");
+ ++i;
+ }
+
+ _dbus_verbose ("%s (%d children)\n",
+ subtree->name, subtree->n_subtrees);
+
+ i = 0;
+ while (i < subtree->n_subtrees)
+ {
+ spew_subtree_recurse (subtree->subtrees[i], indent + 2);
+
+ ++i;
+ }
+}
+
+static void
+spew_tree (DBusObjectTree *tree)
+{
+ spew_subtree_recurse (tree->root, 0);
+}
+#endif
+
+/**
+ * Callback data used in tests
+ */
+typedef struct
+{
+ const char **path; /**< Path */
+ dbus_bool_t handler_fallback; /**< true if the handler may be called as fallback */
+ dbus_bool_t message_handled; /**< Gets set to true if message handler called */
+ dbus_bool_t handler_unregistered; /**< gets set to true if handler is unregistered */
+} TreeTestData;
+
+
+static void
+test_unregister_function (DBusConnection *connection,
+ void *user_data)
+{
+ TreeTestData *ttd = user_data;
+
+ ttd->handler_unregistered = TRUE;
+}
+
+static DBusHandlerResult
+test_message_function (DBusConnection *connection,
+ DBusMessage *message,
+ void *user_data)
+{
+ TreeTestData *ttd = user_data;
+
+ ttd->message_handled = TRUE;
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static dbus_bool_t
+do_register (DBusObjectTree *tree,
+ const char **path,
+ dbus_bool_t fallback,
+ int i,
+ TreeTestData *tree_test_data)
+{
+ DBusObjectPathVTable vtable = { test_unregister_function,
+ test_message_function, NULL };
+
+ tree_test_data[i].message_handled = FALSE;
+ tree_test_data[i].handler_unregistered = FALSE;
+ tree_test_data[i].handler_fallback = fallback;
+ tree_test_data[i].path = path;
+
+ if (!_dbus_object_tree_register (tree, fallback, path,
+ &vtable,
+ &tree_test_data[i],
+ NULL))
+ return FALSE;
+
+ _dbus_assert (_dbus_object_tree_get_user_data_unlocked (tree, path) ==
+ &tree_test_data[i]);
+
+ return TRUE;
+}
+
+static dbus_bool_t
+do_test_dispatch (DBusObjectTree *tree,
+ const char **path,
+ int i,
+ TreeTestData *tree_test_data,
+ int n_test_data)
+{
+ DBusMessage *message;
+ int j;
+ DBusHandlerResult result;
+ char *flat;
+
+ message = NULL;
+
+ flat = flatten_path (path);
+ if (flat == NULL)
+ goto oom;
+
+ message = dbus_message_new_method_call (NULL,
+ flat,
+ "org.freedesktop.TestInterface",
+ "Foo");
+ dbus_free (flat);
+ if (message == NULL)
+ goto oom;
+
+ j = 0;
+ while (j < n_test_data)
+ {
+ tree_test_data[j].message_handled = FALSE;
+ ++j;
+ }
+
+ result = _dbus_object_tree_dispatch_and_unlock (tree, message, NULL);
+ if (result == DBUS_HANDLER_RESULT_NEED_MEMORY)
+ goto oom;
+
+ _dbus_assert (tree_test_data[i].message_handled);
+
+ j = 0;
+ while (j < n_test_data)
+ {
+ if (tree_test_data[j].message_handled)
+ {
+ if (tree_test_data[j].handler_fallback)
+ _dbus_assert (path_contains (tree_test_data[j].path,
+ path) != STR_DIFFERENT);
+ else
+ _dbus_assert (path_contains (tree_test_data[j].path, path) == STR_EQUAL);
+ }
+ else
+ {
+ if (tree_test_data[j].handler_fallback)
+ _dbus_assert (path_contains (tree_test_data[j].path,
+ path) == STR_DIFFERENT);
+ else
+ _dbus_assert (path_contains (tree_test_data[j].path, path) != STR_EQUAL);
+ }
+
+ ++j;
+ }
+
+ dbus_message_unref (message);
+
+ return TRUE;
+
+ oom:
+ if (message)
+ dbus_message_unref (message);
+ return FALSE;
+}
+
+typedef struct
+{
+ const char *path;
+ const char *result[20];
+} DecomposePathTest;
+
+static DecomposePathTest decompose_tests[] = {
+ { "/foo", { "foo", NULL } },
+ { "/foo/bar", { "foo", "bar", NULL } },
+ { "/", { NULL } },
+ { "/a/b", { "a", "b", NULL } },
+ { "/a/b/c", { "a", "b", "c", NULL } },
+ { "/a/b/c/d", { "a", "b", "c", "d", NULL } },
+ { "/foo/bar/q", { "foo", "bar", "q", NULL } },
+ { "/foo/bar/this/is/longer", { "foo", "bar", "this", "is", "longer", NULL } }
+};
+
+/* Return TRUE on success, FALSE on OOM, die with an assertion failure
+ * on failure. */
+static dbus_bool_t
+run_decompose_tests (void)
+{
+ int i;
+
+ i = 0;
+ while (i < _DBUS_N_ELEMENTS (decompose_tests))
+ {
+ char **result;
+ int result_len;
+ int expected_len;
+
+ if (!_dbus_decompose_path (decompose_tests[i].path,
+ strlen (decompose_tests[i].path),
+ &result, &result_len))
+ return FALSE;
+
+ expected_len = _dbus_string_array_length (decompose_tests[i].result);
+
+ if (result_len != (int) _dbus_string_array_length ((const char**)result) ||
+ expected_len != result_len ||
+ path_contains (decompose_tests[i].result,
+ (const char**) result) != STR_EQUAL)
+ {
+ int real_len = _dbus_string_array_length ((const char**)result);
+ _dbus_warn ("Expected decompose of %s to have len %d, returned %d, appears to have %d",
+ decompose_tests[i].path, expected_len, result_len,
+ real_len);
+ _dbus_warn ("Decompose resulted in elements: { ");
+ i = 0;
+ while (i < real_len)
+ {
+ _dbus_warn ("\"%s\"%s", result[i],
+ (i + 1) == real_len ? "" : ", ");
+ ++i;
+ }
+ _dbus_warn ("}");
+ _dbus_test_fatal ("path decompose failed");
+ }
+
+ dbus_free_string_array (result);
+
+ ++i;
+ }
+
+ return TRUE;
+}
+
+static DBusObjectSubtree*
+find_subtree_registered_or_unregistered (DBusObjectTree *tree,
+ const char **path)
+{
+#if VERBOSE_FIND
+ _dbus_verbose ("Looking for exact subtree, registered or unregistered\n");
+#endif
+
+ return find_subtree_recurse (tree->root, path, FALSE, NULL, NULL);
+}
+
+/* Returns TRUE if the right thing happens, but the right thing might
+ * be OOM. */
+static dbus_bool_t
+object_tree_test_iteration (void *data,
+ dbus_bool_t have_memory)
+{
+ const char *path0[] = { NULL };
+ const char *path1[] = { "foo", NULL };
+ const char *path2[] = { "foo", "bar", NULL };
+ const char *path3[] = { "foo", "bar", "baz", NULL };
+ const char *path4[] = { "foo", "bar", "boo", NULL };
+ const char *path5[] = { "blah", NULL };
+ const char *path6[] = { "blah", "boof", NULL };
+ const char *path7[] = { "blah", "boof", "this", "is", "really", "long", NULL };
+ const char *path8[] = { "childless", NULL };
+ const char *path9[] = { "blah", "a", NULL };
+ const char *path10[] = { "blah", "b", NULL };
+ const char *path11[] = { "blah", "c", NULL };
+ const char *path12[] = { "blah", "a", "d", NULL };
+ const char *path13[] = { "blah", "b", "d", NULL };
+ const char *path14[] = { "blah", "c", "d", NULL };
+ DBusObjectPathVTable test_vtable = { NULL, test_message_function, NULL };
+ DBusObjectTree *tree;
+ TreeTestData tree_test_data[9];
+ int i;
+ dbus_bool_t exact_match;
+
+ if (!run_decompose_tests ())
+ return TRUE; /* OOM is OK */
+
+ tree = NULL;
+
+ tree = _dbus_object_tree_new (NULL);
+ if (tree == NULL)
+ goto out;
+
+ if (!do_register (tree, path0, TRUE, 0, tree_test_data))
+ goto out;
+
+ _dbus_assert (find_subtree (tree, path0, NULL));
+ _dbus_assert (!find_subtree (tree, path1, NULL));
+ _dbus_assert (!find_subtree (tree, path2, NULL));
+ _dbus_assert (!find_subtree (tree, path3, NULL));
+ _dbus_assert (!find_subtree (tree, path4, NULL));
+ _dbus_assert (!find_subtree (tree, path5, NULL));
+ _dbus_assert (!find_subtree (tree, path6, NULL));
+ _dbus_assert (!find_subtree (tree, path7, NULL));
+ _dbus_assert (!find_subtree (tree, path8, NULL));
+
+ _dbus_assert (find_handler (tree, path0, &exact_match) && exact_match);
+ _dbus_assert (find_handler (tree, path1, &exact_match) == tree->root && !exact_match);
+ _dbus_assert (find_handler (tree, path2, &exact_match) == tree->root && !exact_match);
+ _dbus_assert (find_handler (tree, path3, &exact_match) == tree->root && !exact_match);
+ _dbus_assert (find_handler (tree, path4, &exact_match) == tree->root && !exact_match);
+ _dbus_assert (find_handler (tree, path5, &exact_match) == tree->root && !exact_match);
+ _dbus_assert (find_handler (tree, path6, &exact_match) == tree->root && !exact_match);
+ _dbus_assert (find_handler (tree, path7, &exact_match) == tree->root && !exact_match);
+ _dbus_assert (find_handler (tree, path8, &exact_match) == tree->root && !exact_match);
+
+ if (!do_register (tree, path1, TRUE, 1, tree_test_data))
+ goto out;
+
+ _dbus_assert (find_subtree (tree, path0, NULL));
+ _dbus_assert (find_subtree (tree, path1, NULL));
+ _dbus_assert (!find_subtree (tree, path2, NULL));
+ _dbus_assert (!find_subtree (tree, path3, NULL));
+ _dbus_assert (!find_subtree (tree, path4, NULL));
+ _dbus_assert (!find_subtree (tree, path5, NULL));
+ _dbus_assert (!find_subtree (tree, path6, NULL));
+ _dbus_assert (!find_subtree (tree, path7, NULL));
+ _dbus_assert (!find_subtree (tree, path8, NULL));
+
+ _dbus_assert (find_handler (tree, path0, &exact_match) && exact_match);
+ _dbus_assert (find_handler (tree, path1, &exact_match) && exact_match);
+ _dbus_assert (find_handler (tree, path2, &exact_match) && !exact_match);
+ _dbus_assert (find_handler (tree, path3, &exact_match) && !exact_match);
+ _dbus_assert (find_handler (tree, path4, &exact_match) && !exact_match);
+ _dbus_assert (find_handler (tree, path5, &exact_match) == tree->root && !exact_match);
+ _dbus_assert (find_handler (tree, path6, &exact_match) == tree->root && !exact_match);
+ _dbus_assert (find_handler (tree, path7, &exact_match) == tree->root && !exact_match);
+ _dbus_assert (find_handler (tree, path8, &exact_match) == tree->root && !exact_match);
+
+ if (!do_register (tree, path2, TRUE, 2, tree_test_data))
+ goto out;
+
+ _dbus_assert (find_subtree (tree, path1, NULL));
+ _dbus_assert (find_subtree (tree, path2, NULL));
+ _dbus_assert (!find_subtree (tree, path3, NULL));
+ _dbus_assert (!find_subtree (tree, path4, NULL));
+ _dbus_assert (!find_subtree (tree, path5, NULL));
+ _dbus_assert (!find_subtree (tree, path6, NULL));
+ _dbus_assert (!find_subtree (tree, path7, NULL));
+ _dbus_assert (!find_subtree (tree, path8, NULL));
+
+ if (!do_register (tree, path3, TRUE, 3, tree_test_data))
+ goto out;
+
+ _dbus_assert (find_subtree (tree, path0, NULL));
+ _dbus_assert (find_subtree (tree, path1, NULL));
+ _dbus_assert (find_subtree (tree, path2, NULL));
+ _dbus_assert (find_subtree (tree, path3, NULL));
+ _dbus_assert (!find_subtree (tree, path4, NULL));
+ _dbus_assert (!find_subtree (tree, path5, NULL));
+ _dbus_assert (!find_subtree (tree, path6, NULL));
+ _dbus_assert (!find_subtree (tree, path7, NULL));
+ _dbus_assert (!find_subtree (tree, path8, NULL));
+
+ if (!do_register (tree, path4, TRUE, 4, tree_test_data))
+ goto out;
+
+ _dbus_assert (find_subtree (tree, path0, NULL));
+ _dbus_assert (find_subtree (tree, path1, NULL));
+ _dbus_assert (find_subtree (tree, path2, NULL));
+ _dbus_assert (find_subtree (tree, path3, NULL));
+ _dbus_assert (find_subtree (tree, path4, NULL));
+ _dbus_assert (!find_subtree (tree, path5, NULL));
+ _dbus_assert (!find_subtree (tree, path6, NULL));
+ _dbus_assert (!find_subtree (tree, path7, NULL));
+ _dbus_assert (!find_subtree (tree, path8, NULL));
+
+ if (!do_register (tree, path5, TRUE, 5, tree_test_data))
+ goto out;
+
+ _dbus_assert (find_subtree (tree, path0, NULL));
+ _dbus_assert (find_subtree (tree, path1, NULL));
+ _dbus_assert (find_subtree (tree, path2, NULL));
+ _dbus_assert (find_subtree (tree, path3, NULL));
+ _dbus_assert (find_subtree (tree, path4, NULL));
+ _dbus_assert (find_subtree (tree, path5, NULL));
+ _dbus_assert (!find_subtree (tree, path6, NULL));
+ _dbus_assert (!find_subtree (tree, path7, NULL));
+ _dbus_assert (!find_subtree (tree, path8, NULL));
+
+ _dbus_assert (find_handler (tree, path0, &exact_match) == tree->root && exact_match);
+ _dbus_assert (find_handler (tree, path1, &exact_match) != tree->root && exact_match);
+ _dbus_assert (find_handler (tree, path2, &exact_match) != tree->root && exact_match);
+ _dbus_assert (find_handler (tree, path3, &exact_match) != tree->root && exact_match);
+ _dbus_assert (find_handler (tree, path4, &exact_match) != tree->root && exact_match);
+ _dbus_assert (find_handler (tree, path5, &exact_match) != tree->root && exact_match);
+ _dbus_assert (find_handler (tree, path6, &exact_match) != tree->root && !exact_match);
+ _dbus_assert (find_handler (tree, path7, &exact_match) != tree->root && !exact_match);
+ _dbus_assert (find_handler (tree, path8, &exact_match) == tree->root && !exact_match);
+
+ if (!do_register (tree, path6, TRUE, 6, tree_test_data))
+ goto out;
+
+ _dbus_assert (find_subtree (tree, path0, NULL));
+ _dbus_assert (find_subtree (tree, path1, NULL));
+ _dbus_assert (find_subtree (tree, path2, NULL));
+ _dbus_assert (find_subtree (tree, path3, NULL));
+ _dbus_assert (find_subtree (tree, path4, NULL));
+ _dbus_assert (find_subtree (tree, path5, NULL));
+ _dbus_assert (find_subtree (tree, path6, NULL));
+ _dbus_assert (!find_subtree (tree, path7, NULL));
+ _dbus_assert (!find_subtree (tree, path8, NULL));
+
+ if (!do_register (tree, path7, TRUE, 7, tree_test_data))
+ goto out;
+
+ _dbus_assert (find_subtree (tree, path0, NULL));
+ _dbus_assert (find_subtree (tree, path1, NULL));
+ _dbus_assert (find_subtree (tree, path2, NULL));
+ _dbus_assert (find_subtree (tree, path3, NULL));
+ _dbus_assert (find_subtree (tree, path4, NULL));
+ _dbus_assert (find_subtree (tree, path5, NULL));
+ _dbus_assert (find_subtree (tree, path6, NULL));
+ _dbus_assert (find_subtree (tree, path7, NULL));
+ _dbus_assert (!find_subtree (tree, path8, NULL));
+
+ if (!do_register (tree, path8, TRUE, 8, tree_test_data))
+ goto out;
+
+ _dbus_assert (find_subtree (tree, path0, NULL));
+ _dbus_assert (find_subtree (tree, path1, NULL));
+ _dbus_assert (find_subtree (tree, path2, NULL));
+ _dbus_assert (find_subtree (tree, path3, NULL));
+ _dbus_assert (find_subtree (tree, path4, NULL));
+ _dbus_assert (find_subtree (tree, path5, NULL));
+ _dbus_assert (find_subtree (tree, path6, NULL));
+ _dbus_assert (find_subtree (tree, path7, NULL));
+ _dbus_assert (find_subtree (tree, path8, NULL));
+
+ _dbus_assert (find_handler (tree, path0, &exact_match) == tree->root && exact_match);
+ _dbus_assert (find_handler (tree, path1, &exact_match) != tree->root && exact_match);
+ _dbus_assert (find_handler (tree, path2, &exact_match) != tree->root && exact_match);
+ _dbus_assert (find_handler (tree, path3, &exact_match) != tree->root && exact_match);
+ _dbus_assert (find_handler (tree, path4, &exact_match) != tree->root && exact_match);
+ _dbus_assert (find_handler (tree, path5, &exact_match) != tree->root && exact_match);
+ _dbus_assert (find_handler (tree, path6, &exact_match) != tree->root && exact_match);
+ _dbus_assert (find_handler (tree, path7, &exact_match) != tree->root && exact_match);
+ _dbus_assert (find_handler (tree, path8, &exact_match) != tree->root && exact_match);
+
+ /* test the list_registered function */
+
+ {
+ const char *root[] = { NULL };
+ char **child_entries;
+ int nb;
+
+ _dbus_object_tree_list_registered_unlocked (tree, path1, &child_entries);
+ if (child_entries != NULL)
+ {
+ nb = _dbus_string_array_length ((const char**)child_entries);
+ _dbus_assert (nb == 1);
+ dbus_free_string_array (child_entries);
+ }
+
+ _dbus_object_tree_list_registered_unlocked (tree, path2, &child_entries);
+ if (child_entries != NULL)
+ {
+ nb = _dbus_string_array_length ((const char**)child_entries);
+ _dbus_assert (nb == 2);
+ dbus_free_string_array (child_entries);
+ }
+
+ _dbus_object_tree_list_registered_unlocked (tree, path8, &child_entries);
+ if (child_entries != NULL)
+ {
+ nb = _dbus_string_array_length ((const char**)child_entries);
+ _dbus_assert (nb == 0);
+ dbus_free_string_array (child_entries);
+ }
+
+ _dbus_object_tree_list_registered_unlocked (tree, root, &child_entries);
+ if (child_entries != NULL)
+ {
+ nb = _dbus_string_array_length ((const char**)child_entries);
+ _dbus_assert (nb == 3);
+ dbus_free_string_array (child_entries);
+ }
+ }
+
+ /* Check that destroying tree calls unregister funcs */
+ _dbus_object_tree_unref (tree);
+
+ i = 0;
+ while (i < (int) _DBUS_N_ELEMENTS (tree_test_data))
+ {
+ _dbus_assert (tree_test_data[i].handler_unregistered);
+ _dbus_assert (!tree_test_data[i].message_handled);
+ ++i;
+ }
+
+ /* Now start again and try the individual unregister function */
+ tree = _dbus_object_tree_new (NULL);
+ if (tree == NULL)
+ goto out;
+
+ if (!do_register (tree, path0, TRUE, 0, tree_test_data))
+ goto out;
+ if (!do_register (tree, path1, TRUE, 1, tree_test_data))
+ goto out;
+ if (!do_register (tree, path2, TRUE, 2, tree_test_data))
+ goto out;
+ if (!do_register (tree, path3, TRUE, 3, tree_test_data))
+ goto out;
+ if (!do_register (tree, path4, TRUE, 4, tree_test_data))
+ goto out;
+ if (!do_register (tree, path5, TRUE, 5, tree_test_data))
+ goto out;
+ if (!do_register (tree, path6, TRUE, 6, tree_test_data))
+ goto out;
+ if (!do_register (tree, path7, TRUE, 7, tree_test_data))
+ goto out;
+ if (!do_register (tree, path8, TRUE, 8, tree_test_data))
+ goto out;
+
+ _dbus_object_tree_unregister_and_unlock (tree, path0);
+ _dbus_assert (_dbus_object_tree_get_user_data_unlocked (tree, path0) == NULL);
+
+ _dbus_assert (!find_subtree (tree, path0, NULL));
+ _dbus_assert (find_subtree (tree, path1, NULL));
+ _dbus_assert (find_subtree (tree, path2, NULL));
+ _dbus_assert (find_subtree (tree, path3, NULL));
+ _dbus_assert (find_subtree (tree, path4, NULL));
+ _dbus_assert (find_subtree (tree, path5, NULL));
+ _dbus_assert (find_subtree (tree, path6, NULL));
+ _dbus_assert (find_subtree (tree, path7, NULL));
+ _dbus_assert (find_subtree (tree, path8, NULL));
+
+ _dbus_object_tree_unregister_and_unlock (tree, path1);
+ _dbus_assert (_dbus_object_tree_get_user_data_unlocked (tree, path1) == NULL);
+
+ _dbus_assert (!find_subtree (tree, path0, NULL));
+ _dbus_assert (!find_subtree (tree, path1, NULL));
+ _dbus_assert (find_subtree (tree, path2, NULL));
+ _dbus_assert (find_subtree (tree, path3, NULL));
+ _dbus_assert (find_subtree (tree, path4, NULL));
+ _dbus_assert (find_subtree (tree, path5, NULL));
+ _dbus_assert (find_subtree (tree, path6, NULL));
+ _dbus_assert (find_subtree (tree, path7, NULL));
+ _dbus_assert (find_subtree (tree, path8, NULL));
+
+ _dbus_object_tree_unregister_and_unlock (tree, path2);
+ _dbus_assert (_dbus_object_tree_get_user_data_unlocked (tree, path2) == NULL);
+
+ _dbus_assert (!find_subtree (tree, path0, NULL));
+ _dbus_assert (!find_subtree (tree, path1, NULL));
+ _dbus_assert (!find_subtree (tree, path2, NULL));
+ _dbus_assert (find_subtree (tree, path3, NULL));
+ _dbus_assert (find_subtree (tree, path4, NULL));
+ _dbus_assert (find_subtree (tree, path5, NULL));
+ _dbus_assert (find_subtree (tree, path6, NULL));
+ _dbus_assert (find_subtree (tree, path7, NULL));
+ _dbus_assert (find_subtree (tree, path8, NULL));
+
+ _dbus_object_tree_unregister_and_unlock (tree, path3);
+ _dbus_assert (_dbus_object_tree_get_user_data_unlocked (tree, path3) == NULL);
+
+ _dbus_assert (!find_subtree (tree, path0, NULL));
+ _dbus_assert (!find_subtree (tree, path1, NULL));
+ _dbus_assert (!find_subtree (tree, path2, NULL));
+ _dbus_assert (!find_subtree (tree, path3, NULL));
+ _dbus_assert (find_subtree (tree, path4, NULL));
+ _dbus_assert (find_subtree (tree, path5, NULL));
+ _dbus_assert (find_subtree (tree, path6, NULL));
+ _dbus_assert (find_subtree (tree, path7, NULL));
+ _dbus_assert (find_subtree (tree, path8, NULL));
+
+ _dbus_object_tree_unregister_and_unlock (tree, path4);
+ _dbus_assert (_dbus_object_tree_get_user_data_unlocked (tree, path4) == NULL);
+
+ _dbus_assert (!find_subtree (tree, path0, NULL));
+ _dbus_assert (!find_subtree (tree, path1, NULL));
+ _dbus_assert (!find_subtree (tree, path2, NULL));
+ _dbus_assert (!find_subtree (tree, path3, NULL));
+ _dbus_assert (!find_subtree (tree, path4, NULL));
+ _dbus_assert (find_subtree (tree, path5, NULL));
+ _dbus_assert (find_subtree (tree, path6, NULL));
+ _dbus_assert (find_subtree (tree, path7, NULL));
+ _dbus_assert (find_subtree (tree, path8, NULL));
+
+ _dbus_object_tree_unregister_and_unlock (tree, path5);
+ _dbus_assert (_dbus_object_tree_get_user_data_unlocked (tree, path5) == NULL);
+
+ _dbus_assert (!find_subtree (tree, path0, NULL));
+ _dbus_assert (!find_subtree (tree, path1, NULL));
+ _dbus_assert (!find_subtree (tree, path2, NULL));
+ _dbus_assert (!find_subtree (tree, path3, NULL));
+ _dbus_assert (!find_subtree (tree, path4, NULL));
+ _dbus_assert (!find_subtree (tree, path5, NULL));
+ _dbus_assert (find_subtree (tree, path6, NULL));
+ _dbus_assert (find_subtree (tree, path7, NULL));
+ _dbus_assert (find_subtree (tree, path8, NULL));
+
+ _dbus_object_tree_unregister_and_unlock (tree, path6);
+ _dbus_assert (_dbus_object_tree_get_user_data_unlocked (tree, path6) == NULL);
+
+ _dbus_assert (!find_subtree (tree, path0, NULL));
+ _dbus_assert (!find_subtree (tree, path1, NULL));
+ _dbus_assert (!find_subtree (tree, path2, NULL));
+ _dbus_assert (!find_subtree (tree, path3, NULL));
+ _dbus_assert (!find_subtree (tree, path4, NULL));
+ _dbus_assert (!find_subtree (tree, path5, NULL));
+ _dbus_assert (!find_subtree (tree, path6, NULL));
+ _dbus_assert (find_subtree (tree, path7, NULL));
+ _dbus_assert (find_subtree (tree, path8, NULL));
+
+ _dbus_object_tree_unregister_and_unlock (tree, path7);
+ _dbus_assert (_dbus_object_tree_get_user_data_unlocked (tree, path7) == NULL);
+
+ _dbus_assert (!find_subtree (tree, path0, NULL));
+ _dbus_assert (!find_subtree (tree, path1, NULL));
+ _dbus_assert (!find_subtree (tree, path2, NULL));
+ _dbus_assert (!find_subtree (tree, path3, NULL));
+ _dbus_assert (!find_subtree (tree, path4, NULL));
+ _dbus_assert (!find_subtree (tree, path5, NULL));
+ _dbus_assert (!find_subtree (tree, path6, NULL));
+ _dbus_assert (!find_subtree (tree, path7, NULL));
+ _dbus_assert (find_subtree (tree, path8, NULL));
+
+ _dbus_object_tree_unregister_and_unlock (tree, path8);
+ _dbus_assert (_dbus_object_tree_get_user_data_unlocked (tree, path8) == NULL);
+
+ _dbus_assert (!find_subtree (tree, path0, NULL));
+ _dbus_assert (!find_subtree (tree, path1, NULL));
+ _dbus_assert (!find_subtree (tree, path2, NULL));
+ _dbus_assert (!find_subtree (tree, path3, NULL));
+ _dbus_assert (!find_subtree (tree, path4, NULL));
+ _dbus_assert (!find_subtree (tree, path5, NULL));
+ _dbus_assert (!find_subtree (tree, path6, NULL));
+ _dbus_assert (!find_subtree (tree, path7, NULL));
+ _dbus_assert (!find_subtree (tree, path8, NULL));
+
+ i = 0;
+ while (i < (int) _DBUS_N_ELEMENTS (tree_test_data))
+ {
+ _dbus_assert (tree_test_data[i].handler_unregistered);
+ _dbus_assert (!tree_test_data[i].message_handled);
+ ++i;
+ }
+
+ /* Test removal of newly-childless unregistered nodes */
+ if (!do_register (tree, path2, TRUE, 2, tree_test_data))
+ goto out;
+
+ _dbus_object_tree_unregister_and_unlock (tree, path2);
+ _dbus_assert (!find_subtree_registered_or_unregistered (tree, path2));
+ _dbus_assert (!find_subtree_registered_or_unregistered (tree, path1));
+ _dbus_assert (find_subtree_registered_or_unregistered (tree, path0));
+
+ /* Test that unregistered parents cannot be freed out from under their
+ children */
+ if (!do_register (tree, path2, TRUE, 2, tree_test_data))
+ goto out;
+
+ _dbus_assert (!find_subtree (tree, path1, NULL));
+ _dbus_assert (find_subtree_registered_or_unregistered (tree, path1));
+ _dbus_assert (find_subtree_registered_or_unregistered (tree, path0));
+
+#if 0
+ /* This triggers the "Attempted to unregister path ..." warning message */
+ _dbus_object_tree_unregister_and_unlock (tree, path1);
+#endif
+ _dbus_assert (find_subtree (tree, path2, NULL));
+ _dbus_assert (!find_subtree (tree, path1, NULL));
+ _dbus_assert (find_subtree_registered_or_unregistered (tree, path1));
+ _dbus_assert (find_subtree_registered_or_unregistered (tree, path0));
+
+ _dbus_object_tree_unregister_and_unlock (tree, path2);
+ _dbus_assert (!find_subtree (tree, path2, NULL));
+ _dbus_assert (!find_subtree_registered_or_unregistered (tree, path2));
+ _dbus_assert (!find_subtree_registered_or_unregistered (tree, path1));
+ _dbus_assert (find_subtree_registered_or_unregistered (tree, path0));
+
+ /* Test that registered parents cannot be freed out from under their
+ children, and that if they are unregistered before their children, they
+ are still freed when their children are unregistered */
+ if (!do_register (tree, path1, TRUE, 1, tree_test_data))
+ goto out;
+ if (!do_register (tree, path2, TRUE, 2, tree_test_data))
+ goto out;
+
+ _dbus_assert (find_subtree (tree, path1, NULL));
+ _dbus_assert (find_subtree (tree, path2, NULL));
+
+ _dbus_object_tree_unregister_and_unlock (tree, path1);
+ _dbus_assert (!find_subtree (tree, path1, NULL));
+ _dbus_assert (find_subtree (tree, path2, NULL));
+ _dbus_assert (find_subtree_registered_or_unregistered (tree, path1));
+ _dbus_assert (find_subtree_registered_or_unregistered (tree, path0));
+
+ _dbus_object_tree_unregister_and_unlock (tree, path2);
+ _dbus_assert (!find_subtree (tree, path1, NULL));
+ _dbus_assert (!find_subtree_registered_or_unregistered (tree, path1));
+ _dbus_assert (!find_subtree (tree, path2, NULL));
+ _dbus_assert (!find_subtree_registered_or_unregistered (tree, path2));
+ _dbus_assert (find_subtree_registered_or_unregistered (tree, path0));
+
+ /* Test with NULL unregister_function and user_data */
+ if (!_dbus_object_tree_register (tree, TRUE, path2,
+ &test_vtable,
+ NULL,
+ NULL))
+ goto out;
+
+ _dbus_assert (_dbus_object_tree_get_user_data_unlocked (tree, path2) == NULL);
+ _dbus_object_tree_unregister_and_unlock (tree, path2);
+ _dbus_assert (!find_subtree (tree, path2, NULL));
+ _dbus_assert (!find_subtree_registered_or_unregistered (tree, path2));
+ _dbus_assert (!find_subtree_registered_or_unregistered (tree, path1));
+ _dbus_assert (find_subtree_registered_or_unregistered (tree, path0));
+
+ /* Test freeing a long path */
+ if (!do_register (tree, path3, TRUE, 3, tree_test_data))
+ goto out;
+
+ _dbus_object_tree_unregister_and_unlock (tree, path3);
+ _dbus_assert (!find_subtree (tree, path3, NULL));
+ _dbus_assert (!find_subtree_registered_or_unregistered (tree, path3));
+ _dbus_assert (!find_subtree_registered_or_unregistered (tree, path2));
+ _dbus_assert (!find_subtree_registered_or_unregistered (tree, path1));
+ _dbus_assert (find_subtree_registered_or_unregistered (tree, path0));
+
+ /* Test freeing multiple children from the same path */
+ if (!do_register (tree, path3, TRUE, 3, tree_test_data))
+ goto out;
+ if (!do_register (tree, path4, TRUE, 4, tree_test_data))
+ goto out;
+
+ _dbus_assert (find_subtree (tree, path3, NULL));
+ _dbus_assert (find_subtree (tree, path4, NULL));
+
+ _dbus_object_tree_unregister_and_unlock (tree, path3);
+ _dbus_assert (!find_subtree (tree, path3, NULL));
+ _dbus_assert (!find_subtree_registered_or_unregistered (tree, path3));
+ _dbus_assert (find_subtree (tree, path4, NULL));
+ _dbus_assert (find_subtree_registered_or_unregistered (tree, path4));
+ _dbus_assert (find_subtree_registered_or_unregistered (tree, path2));
+ _dbus_assert (find_subtree_registered_or_unregistered (tree, path1));
+
+ _dbus_object_tree_unregister_and_unlock (tree, path4);
+ _dbus_assert (!find_subtree (tree, path4, NULL));
+ _dbus_assert (!find_subtree_registered_or_unregistered (tree, path4));
+ _dbus_assert (!find_subtree (tree, path3, NULL));
+ _dbus_assert (!find_subtree_registered_or_unregistered (tree, path3));
+ _dbus_assert (!find_subtree_registered_or_unregistered (tree, path2));
+ _dbus_assert (!find_subtree_registered_or_unregistered (tree, path1));
+
+ /* Test subtree removal */
+ if (!_dbus_object_tree_register (tree, TRUE, path12,
+ &test_vtable,
+ NULL,
+ NULL))
+ goto out;
+
+ _dbus_assert (find_subtree (tree, path12, NULL));
+
+ if (!_dbus_object_tree_register (tree, TRUE, path13,
+ &test_vtable,
+ NULL,
+ NULL))
+ goto out;
+
+ _dbus_assert (find_subtree (tree, path13, NULL));
+
+ if (!_dbus_object_tree_register (tree, TRUE, path14,
+ &test_vtable,
+ NULL,
+ NULL))
+ goto out;
+
+ _dbus_assert (find_subtree (tree, path14, NULL));
+
+ _dbus_object_tree_unregister_and_unlock (tree, path12);
+
+ _dbus_assert (!find_subtree_registered_or_unregistered (tree, path12));
+ _dbus_assert (find_subtree (tree, path13, NULL));
+ _dbus_assert (find_subtree (tree, path14, NULL));
+ _dbus_assert (!find_subtree_registered_or_unregistered (tree, path9));
+ _dbus_assert (find_subtree_registered_or_unregistered (tree, path5));
+
+ if (!_dbus_object_tree_register (tree, TRUE, path12,
+ &test_vtable,
+ NULL,
+ NULL))
+ goto out;
+
+ _dbus_assert (find_subtree (tree, path12, NULL));
+
+ _dbus_object_tree_unregister_and_unlock (tree, path13);
+
+ _dbus_assert (find_subtree (tree, path12, NULL));
+ _dbus_assert (!find_subtree_registered_or_unregistered (tree, path13));
+ _dbus_assert (find_subtree (tree, path14, NULL));
+ _dbus_assert (!find_subtree_registered_or_unregistered (tree, path10));
+ _dbus_assert (find_subtree_registered_or_unregistered (tree, path5));
+
+ if (!_dbus_object_tree_register (tree, TRUE, path13,
+ &test_vtable,
+ NULL,
+ NULL))
+ goto out;
+
+ _dbus_assert (find_subtree (tree, path13, NULL));
+
+ _dbus_object_tree_unregister_and_unlock (tree, path14);
+
+ _dbus_assert (find_subtree (tree, path12, NULL));
+ _dbus_assert (find_subtree (tree, path13, NULL));
+ _dbus_assert (!find_subtree_registered_or_unregistered (tree, path14));
+ _dbus_assert (!find_subtree_registered_or_unregistered (tree, path11));
+ _dbus_assert (find_subtree_registered_or_unregistered (tree, path5));
+
+ _dbus_object_tree_unregister_and_unlock (tree, path12);
+
+ _dbus_assert (!find_subtree_registered_or_unregistered (tree, path12));
+ _dbus_assert (!find_subtree_registered_or_unregistered (tree, path9));
+ _dbus_assert (find_subtree_registered_or_unregistered (tree, path5));
+
+ _dbus_object_tree_unregister_and_unlock (tree, path13);
+
+ _dbus_assert (!find_subtree_registered_or_unregistered (tree, path13));
+ _dbus_assert (!find_subtree_registered_or_unregistered (tree, path10));
+ _dbus_assert (!find_subtree_registered_or_unregistered (tree, path5));
+
+#if 0
+ /* Test attempting to unregister non-existent paths. These trigger
+ "Attempted to unregister path ..." warning messages */
+ _dbus_object_tree_unregister_and_unlock (tree, path0);
+ _dbus_object_tree_unregister_and_unlock (tree, path1);
+ _dbus_object_tree_unregister_and_unlock (tree, path2);
+ _dbus_object_tree_unregister_and_unlock (tree, path3);
+ _dbus_object_tree_unregister_and_unlock (tree, path4);
+#endif
+
+ /* Register it all again, and test dispatch */
+
+ if (!do_register (tree, path0, TRUE, 0, tree_test_data))
+ goto out;
+ if (!do_register (tree, path1, FALSE, 1, tree_test_data))
+ goto out;
+ if (!do_register (tree, path2, TRUE, 2, tree_test_data))
+ goto out;
+ if (!do_register (tree, path3, TRUE, 3, tree_test_data))
+ goto out;
+ if (!do_register (tree, path4, TRUE, 4, tree_test_data))
+ goto out;
+ if (!do_register (tree, path5, TRUE, 5, tree_test_data))
+ goto out;
+ if (!do_register (tree, path6, FALSE, 6, tree_test_data))
+ goto out;
+ if (!do_register (tree, path7, TRUE, 7, tree_test_data))
+ goto out;
+ if (!do_register (tree, path8, TRUE, 8, tree_test_data))
+ goto out;
+
+#if 0
+ spew_tree (tree);
+#endif
+
+ if (!do_test_dispatch (tree, path0, 0, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data)))
+ goto out;
+ if (!do_test_dispatch (tree, path1, 1, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data)))
+ goto out;
+ if (!do_test_dispatch (tree, path2, 2, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data)))
+ goto out;
+ if (!do_test_dispatch (tree, path3, 3, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data)))
+ goto out;
+ if (!do_test_dispatch (tree, path4, 4, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data)))
+ goto out;
+ if (!do_test_dispatch (tree, path5, 5, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data)))
+ goto out;
+ if (!do_test_dispatch (tree, path6, 6, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data)))
+ goto out;
+ if (!do_test_dispatch (tree, path7, 7, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data)))
+ goto out;
+ if (!do_test_dispatch (tree, path8, 8, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data)))
+ goto out;
+
+ out:
+ if (tree)
+ {
+ /* test ref */
+ _dbus_object_tree_ref (tree);
+ _dbus_object_tree_unref (tree);
+ _dbus_object_tree_unref (tree);
+ }
+
+ return TRUE;
+}
+
+/**
+ * @ingroup DBusObjectTree
+ * Unit test for DBusObjectTree
+ * @returns #TRUE on success.
+ */
+dbus_bool_t
+_dbus_object_tree_test (const char *test_data_dir _DBUS_GNUC_UNUSED)
+{
+ return _dbus_test_oom_handling ("object tree",
+ object_tree_test_iteration,
+ NULL);
+}
+
+#endif /* !DOXYGEN_SHOULD_SKIP_THIS */
+
+#endif /* DBUS_ENABLE_EMBEDDED_TESTS */
diff --git a/src/3rdparty/libdbus/dbus/dbus-object-tree.h b/src/3rdparty/libdbus/dbus/dbus-object-tree.h
new file mode 100644
index 00000000..245f9336
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-object-tree.h
@@ -0,0 +1,65 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-object-tree.h DBusObjectTree (internals of DBusConnection)
+ *
+ * Copyright (C) 2003 Red Hat Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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
+ *
+ */
+#ifndef DBUS_OBJECT_TREE_H
+#define DBUS_OBJECT_TREE_H
+
+#include <dbus/dbus-connection.h>
+
+DBUS_BEGIN_DECLS
+
+typedef struct DBusObjectTree DBusObjectTree;
+
+DBusObjectTree* _dbus_object_tree_new (DBusConnection *connection);
+DBusObjectTree* _dbus_object_tree_ref (DBusObjectTree *tree);
+void _dbus_object_tree_unref (DBusObjectTree *tree);
+
+dbus_bool_t _dbus_object_tree_register (DBusObjectTree *tree,
+ dbus_bool_t fallback,
+ const char **path,
+ const DBusObjectPathVTable *vtable,
+ void *user_data,
+ DBusError *error);
+void _dbus_object_tree_unregister_and_unlock (DBusObjectTree *tree,
+ const char **path);
+DBusHandlerResult _dbus_object_tree_dispatch_and_unlock (DBusObjectTree *tree,
+ DBusMessage *message,
+ dbus_bool_t *found_object);
+void* _dbus_object_tree_get_user_data_unlocked (DBusObjectTree *tree,
+ const char **path);
+void _dbus_object_tree_free_all_unlocked (DBusObjectTree *tree);
+
+
+dbus_bool_t _dbus_object_tree_list_registered_and_unlock (DBusObjectTree *tree,
+ const char **parent_path,
+ char ***child_entries);
+
+dbus_bool_t _dbus_decompose_path (const char *data,
+ int len,
+ char ***path,
+ int *path_len);
+
+DBUS_END_DECLS
+
+#endif /* DBUS_OBJECT_TREE_H */
diff --git a/src/3rdparty/libdbus/dbus/dbus-pending-call-internal.h b/src/3rdparty/libdbus/dbus/dbus-pending-call-internal.h
new file mode 100644
index 00000000..a3734937
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-pending-call-internal.h
@@ -0,0 +1,73 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-pending-call-internal.h DBusPendingCall internal interfaces
+ *
+ * Copyright (C) 2002 Red Hat Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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
+ *
+ */
+#ifndef DBUS_PENDING_CALL_INTERNAL_H
+#define DBUS_PENDING_CALL_INTERNAL_H
+
+
+#include <dbus/dbus-internals.h>
+#include <dbus/dbus-message.h>
+#include <dbus/dbus-connection.h>
+#include <dbus/dbus-list.h>
+
+DBUS_BEGIN_DECLS
+
+dbus_bool_t _dbus_pending_call_is_timeout_added_unlocked (DBusPendingCall *pending);
+void _dbus_pending_call_set_timeout_added_unlocked (DBusPendingCall *pending,
+ dbus_bool_t is_added);
+DBusTimeout * _dbus_pending_call_get_timeout_unlocked (DBusPendingCall *pending);
+dbus_uint32_t _dbus_pending_call_get_reply_serial_unlocked (DBusPendingCall *pending);
+void _dbus_pending_call_set_reply_serial_unlocked (DBusPendingCall *pending,
+ dbus_uint32_t serial);
+DBusConnection * _dbus_pending_call_get_connection_and_lock (DBusPendingCall *pending);
+DBusConnection * _dbus_pending_call_get_connection_unlocked (DBusPendingCall *pending);
+dbus_bool_t _dbus_pending_call_get_completed_unlocked (DBusPendingCall *pending);
+
+void _dbus_pending_call_start_completion_unlocked (DBusPendingCall *pending);
+void _dbus_pending_call_finish_completion (DBusPendingCall *pending);
+
+void _dbus_pending_call_set_reply_unlocked (DBusPendingCall *pending,
+ DBusMessage *message);
+void _dbus_pending_call_queue_timeout_error_unlocked (DBusPendingCall *pending,
+ DBusConnection *connection);
+dbus_bool_t _dbus_pending_call_set_timeout_error_unlocked (DBusPendingCall *pending,
+ DBusMessage *message,
+ dbus_uint32_t serial);
+DBUS_PRIVATE_EXPORT
+DBusPendingCall* _dbus_pending_call_new_unlocked (DBusConnection *connection,
+ int timeout_milliseconds,
+ DBusTimeoutHandler timeout_handler);
+DBUS_PRIVATE_EXPORT
+DBusPendingCall* _dbus_pending_call_ref_unlocked (DBusPendingCall *pending);
+DBUS_PRIVATE_EXPORT
+void _dbus_pending_call_unref_and_unlock (DBusPendingCall *pending);
+dbus_bool_t _dbus_pending_call_set_data_unlocked (DBusPendingCall *pending,
+ dbus_int32_t slot,
+ void *data,
+ DBusFreeFunction free_data_func);
+
+
+DBUS_END_DECLS
+
+#endif /* DBUS_PENDING_CALL_INTERNAL_H */
diff --git a/src/3rdparty/libdbus/dbus/dbus-pending-call.c b/src/3rdparty/libdbus/dbus/dbus-pending-call.c
new file mode 100644
index 00000000..f0b39d90
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-pending-call.c
@@ -0,0 +1,872 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-pending-call.c Object representing a call in progress.
+ *
+ * Copyright (C) 2002, 2003 Red Hat Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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-connection-internal.h"
+#include "dbus-message-internal.h"
+#include "dbus-pending-call-internal.h"
+#include "dbus-pending-call.h"
+#include "dbus-list.h"
+#include "dbus-threads.h"
+#include "dbus-test.h"
+
+/**
+ * @defgroup DBusPendingCallInternals DBusPendingCall implementation details
+ * @ingroup DBusInternals
+ * @brief DBusPendingCall private implementation details.
+ *
+ * The guts of DBusPendingCall and its methods.
+ *
+ * @{
+ */
+
+/**
+ * @brief Internals of DBusPendingCall
+ *
+ * Opaque object representing a reply message that we're waiting for.
+ */
+
+/**
+ * shorter and more visible way to write _dbus_connection_lock()
+ */
+#define CONNECTION_LOCK(connection) _dbus_connection_lock(connection)
+/**
+ * shorter and more visible way to write _dbus_connection_unlock()
+ */
+#define CONNECTION_UNLOCK(connection) _dbus_connection_unlock(connection)
+
+/**
+ * Implementation details of #DBusPendingCall - all fields are private.
+ */
+struct DBusPendingCall
+{
+ DBusAtomic refcount; /**< reference count */
+
+ DBusDataSlotList slot_list; /**< Data stored by allocated integer ID */
+
+ DBusPendingCallNotifyFunction function; /**< Notifier when reply arrives. */
+
+ DBusConnection *connection; /**< Connections we're associated with */
+ DBusMessage *reply; /**< Reply (after we've received it) */
+ DBusTimeout *timeout; /**< Timeout */
+
+ DBusList *timeout_link; /**< Preallocated timeout response */
+
+ dbus_uint32_t reply_serial; /**< Expected serial of reply */
+
+ /**
+ * TRUE if some thread has taken responsibility for completing this
+ * pending call: either the pending call has completed, or it is about
+ * to be completed. Protected by the connection lock.
+ */
+ unsigned int completed : 1;
+ /**
+ * TRUE if we have added the timeout. Protected by the connection lock.
+ */
+ unsigned int timeout_added : 1;
+};
+
+static void
+_dbus_pending_call_trace_ref (DBusPendingCall *pending_call,
+ int old_refcount,
+ int new_refcount,
+ const char *why)
+{
+#ifdef DBUS_ENABLE_VERBOSE_MODE
+ static int enabled = -1;
+
+ _dbus_trace_ref ("DBusPendingCall", pending_call, old_refcount,
+ new_refcount, why, "DBUS_PENDING_CALL_TRACE", &enabled);
+#endif
+}
+
+/* protected by _DBUS_LOCK_pending_call_slots */
+static dbus_int32_t notify_user_data_slot = -1;
+
+/**
+ * Creates a new pending reply object.
+ *
+ * @param connection connection where reply will arrive
+ * @param timeout_milliseconds length of timeout, -1 (or
+ * #DBUS_TIMEOUT_USE_DEFAULT) for default,
+ * #DBUS_TIMEOUT_INFINITE for no timeout
+ * @param timeout_handler timeout handler, takes pending call as data
+ * @returns a new #DBusPendingCall or #NULL if no memory.
+ */
+DBusPendingCall*
+_dbus_pending_call_new_unlocked (DBusConnection *connection,
+ int timeout_milliseconds,
+ DBusTimeoutHandler timeout_handler)
+{
+ DBusPendingCall *pending;
+ DBusTimeout *timeout;
+
+ _dbus_assert (timeout_milliseconds >= 0 || timeout_milliseconds == -1);
+
+ if (timeout_milliseconds == -1)
+ timeout_milliseconds = _DBUS_DEFAULT_TIMEOUT_VALUE;
+
+ if (!dbus_pending_call_allocate_data_slot (&notify_user_data_slot))
+ return NULL;
+
+ pending = dbus_new0 (DBusPendingCall, 1);
+
+ if (pending == NULL)
+ {
+ dbus_pending_call_free_data_slot (&notify_user_data_slot);
+ return NULL;
+ }
+
+ if (timeout_milliseconds != DBUS_TIMEOUT_INFINITE)
+ {
+ timeout = _dbus_timeout_new (timeout_milliseconds,
+ timeout_handler,
+ pending, NULL);
+
+ if (timeout == NULL)
+ {
+ dbus_pending_call_free_data_slot (&notify_user_data_slot);
+ dbus_free (pending);
+ return NULL;
+ }
+
+ pending->timeout = timeout;
+ }
+ else
+ {
+ pending->timeout = NULL;
+ }
+
+ _dbus_atomic_inc (&pending->refcount);
+ pending->connection = connection;
+ _dbus_connection_ref_unlocked (pending->connection);
+
+ _dbus_data_slot_list_init (&pending->slot_list);
+
+ _dbus_pending_call_trace_ref (pending, 0, 1, "new_unlocked");
+
+ return pending;
+}
+
+/**
+ * Sets the reply of a pending call with the given message,
+ * or if the message is #NULL, by timing out the pending call.
+ *
+ * @param pending the pending call
+ * @param message the message to complete the call with, or #NULL
+ * to time out the call
+ */
+void
+_dbus_pending_call_set_reply_unlocked (DBusPendingCall *pending,
+ DBusMessage *message)
+{
+ if (message == NULL)
+ {
+ message = pending->timeout_link->data;
+ _dbus_list_clear (&pending->timeout_link);
+ }
+ else
+ dbus_message_ref (message);
+
+ _dbus_verbose (" handing message %p (%s) to pending call serial %u\n",
+ message,
+ dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_METHOD_RETURN ?
+ "method return" :
+ dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_ERROR ?
+ "error" : "other type",
+ pending->reply_serial);
+
+ _dbus_assert (pending->reply == NULL);
+ _dbus_assert (pending->reply_serial == dbus_message_get_reply_serial (message));
+ pending->reply = message;
+}
+
+/**
+ * Sets the pending call to completed
+ *
+ * This method is called with the connection lock held, to protect
+ * pending->completed. It must be paired with a call to
+ * _dbus_pending_call_finish_completion() after the connection lock has
+ * been released.
+ *
+ * @param pending the pending call
+ */
+void
+_dbus_pending_call_start_completion_unlocked (DBusPendingCall *pending)
+{
+ _dbus_assert (!pending->completed);
+
+ pending->completed = TRUE;
+}
+
+/**
+ * Call the notifier function for the pending call.
+ *
+ * This method must be called after the connection lock has been
+ * released, and must be paired with a call to
+ * _dbus_pending_call_start_completion_unlocked().
+ *
+ * @param pending the pending call
+ */
+void
+_dbus_pending_call_finish_completion (DBusPendingCall *pending)
+{
+ _dbus_assert (pending->completed);
+
+ if (pending->function)
+ {
+ void *user_data;
+ user_data = dbus_pending_call_get_data (pending,
+ notify_user_data_slot);
+
+ (* pending->function) (pending, user_data);
+ }
+}
+
+/**
+ * If the pending call hasn't been timed out, add its timeout
+ * error reply to the connection's incoming message queue.
+ *
+ * @param pending the pending call
+ * @param connection the connection the call was sent to
+ */
+void
+_dbus_pending_call_queue_timeout_error_unlocked (DBusPendingCall *pending,
+ DBusConnection *connection)
+{
+ _dbus_assert (connection == pending->connection);
+
+ if (pending->timeout_link)
+ {
+ _dbus_connection_queue_synthesized_message_link (connection,
+ pending->timeout_link);
+ pending->timeout_link = NULL;
+ }
+}
+
+/**
+ * Checks to see if a timeout has been added
+ *
+ * @param pending the pending_call
+ * @returns #TRUE if there is a timeout or #FALSE if not
+ */
+dbus_bool_t
+_dbus_pending_call_is_timeout_added_unlocked (DBusPendingCall *pending)
+{
+ _dbus_assert (pending != NULL);
+
+ return pending->timeout_added;
+}
+
+
+/**
+ * Sets wether the timeout has been added
+ *
+ * @param pending the pending_call
+ * @param is_added whether or not a timeout is added
+ */
+void
+_dbus_pending_call_set_timeout_added_unlocked (DBusPendingCall *pending,
+ dbus_bool_t is_added)
+{
+ _dbus_assert (pending != NULL);
+
+ pending->timeout_added = is_added;
+}
+
+
+/**
+ * Retrives the timeout
+ *
+ * @param pending the pending_call
+ * @returns a timeout object or NULL if call has no timeout
+ */
+DBusTimeout *
+_dbus_pending_call_get_timeout_unlocked (DBusPendingCall *pending)
+{
+ _dbus_assert (pending != NULL);
+
+ return pending->timeout;
+}
+
+/**
+ * Gets the reply's serial number
+ *
+ * @param pending the pending_call
+ * @returns a serial number for the reply or 0
+ */
+dbus_uint32_t
+_dbus_pending_call_get_reply_serial_unlocked (DBusPendingCall *pending)
+{
+ _dbus_assert (pending != NULL);
+
+ return pending->reply_serial;
+}
+
+/**
+ * Sets the reply's serial number
+ *
+ * @param pending the pending_call
+ * @param serial the serial number
+ */
+void
+_dbus_pending_call_set_reply_serial_unlocked (DBusPendingCall *pending,
+ dbus_uint32_t serial)
+{
+ _dbus_assert (pending != NULL);
+ _dbus_assert (pending->reply_serial == 0);
+
+ pending->reply_serial = serial;
+}
+
+/**
+ * Gets the connection associated with this pending call.
+ *
+ * @param pending the pending_call
+ * @returns the connection associated with the pending call
+ */
+DBusConnection *
+_dbus_pending_call_get_connection_and_lock (DBusPendingCall *pending)
+{
+ _dbus_assert (pending != NULL);
+
+ CONNECTION_LOCK (pending->connection);
+ return pending->connection;
+}
+
+/**
+ * Gets the connection associated with this pending call.
+ *
+ * @param pending the pending_call
+ * @returns the connection associated with the pending call
+ */
+DBusConnection *
+_dbus_pending_call_get_connection_unlocked (DBusPendingCall *pending)
+{
+ _dbus_assert (pending != NULL);
+
+ return pending->connection;
+}
+
+/**
+ * Sets the reply message associated with the pending call to a timeout error
+ *
+ * @param pending the pending_call
+ * @param message the message we are sending the error reply to
+ * @param serial serial number for the reply
+ * @return #FALSE on OOM
+ */
+dbus_bool_t
+_dbus_pending_call_set_timeout_error_unlocked (DBusPendingCall *pending,
+ DBusMessage *message,
+ dbus_uint32_t serial)
+{
+ DBusList *reply_link;
+ DBusMessage *reply;
+
+ reply = dbus_message_new_error (message, DBUS_ERROR_NO_REPLY,
+ "Did not receive a reply. Possible causes include: "
+ "the remote application did not send a reply, "
+ "the message bus security policy blocked the reply, "
+ "the reply timeout expired, or "
+ "the network connection was broken.");
+ if (reply == NULL)
+ return FALSE;
+
+ reply_link = _dbus_list_alloc_link (reply);
+ if (reply_link == NULL)
+ {
+ /* it's OK to unref this, nothing that could have attached a callback
+ * has ever seen it */
+ dbus_message_unref (reply);
+ return FALSE;
+ }
+
+ pending->timeout_link = reply_link;
+
+ _dbus_pending_call_set_reply_serial_unlocked (pending, serial);
+
+ return TRUE;
+}
+
+/**
+ * Increments the reference count on a pending call,
+ * while the lock on its connection is already held.
+ *
+ * @param pending the pending call object
+ * @returns the pending call object
+ */
+DBusPendingCall *
+_dbus_pending_call_ref_unlocked (DBusPendingCall *pending)
+{
+ dbus_int32_t old_refcount;
+
+ old_refcount = _dbus_atomic_inc (&pending->refcount);
+ _dbus_pending_call_trace_ref (pending, old_refcount, old_refcount + 1,
+ "ref_unlocked");
+
+ return pending;
+}
+
+
+static void
+_dbus_pending_call_last_unref (DBusPendingCall *pending)
+{
+ DBusConnection *connection;
+
+ /* If we get here, we should be already detached
+ * from the connection, or never attached.
+ */
+ _dbus_assert (!pending->timeout_added);
+
+ connection = pending->connection;
+
+ /* this assumes we aren't holding connection lock... */
+ _dbus_data_slot_list_free (&pending->slot_list);
+
+ if (pending->timeout != NULL)
+ _dbus_timeout_unref (pending->timeout);
+
+ if (pending->timeout_link)
+ {
+ dbus_message_unref ((DBusMessage *)pending->timeout_link->data);
+ _dbus_list_free_link (pending->timeout_link);
+ pending->timeout_link = NULL;
+ }
+
+ if (pending->reply)
+ {
+ dbus_message_unref (pending->reply);
+ pending->reply = NULL;
+ }
+
+ dbus_free (pending);
+
+ dbus_pending_call_free_data_slot (&notify_user_data_slot);
+
+ /* connection lock should not be held. */
+ /* Free the connection last to avoid a weird state while
+ * calling out to application code where the pending exists
+ * but not the connection.
+ */
+ dbus_connection_unref (connection);
+}
+
+/**
+ * Decrements the reference count on a pending call,
+ * freeing it if the count reaches 0. Assumes
+ * connection lock is already held.
+ *
+ * @param pending the pending call object
+ */
+void
+_dbus_pending_call_unref_and_unlock (DBusPendingCall *pending)
+{
+ dbus_int32_t old_refcount;
+
+ old_refcount = _dbus_atomic_dec (&pending->refcount);
+ _dbus_assert (old_refcount > 0);
+ _dbus_pending_call_trace_ref (pending, old_refcount,
+ old_refcount - 1, "unref_and_unlock");
+
+ CONNECTION_UNLOCK (pending->connection);
+
+ if (old_refcount == 1)
+ _dbus_pending_call_last_unref (pending);
+}
+
+/**
+ * Checks whether the pending call has received a reply
+ * yet, or not. Assumes connection lock is held.
+ *
+ * @param pending the pending call
+ * @returns #TRUE if a reply has been received
+ */
+dbus_bool_t
+_dbus_pending_call_get_completed_unlocked (DBusPendingCall *pending)
+{
+ return pending->completed;
+}
+
+static DBusDataSlotAllocator slot_allocator =
+ _DBUS_DATA_SLOT_ALLOCATOR_INIT (_DBUS_LOCK_NAME (pending_call_slots));
+
+/**
+ * Stores a pointer on a #DBusPendingCall, along
+ * with an optional function to be used for freeing
+ * the data when the data is set again, or when
+ * the pending call is finalized. The slot number
+ * must have been allocated with dbus_pending_call_allocate_data_slot().
+ *
+ * @param pending the pending_call
+ * @param slot the slot number
+ * @param data the data to store
+ * @param free_data_func finalizer function for the data
+ * @returns #TRUE if there was enough memory to store the data
+ */
+dbus_bool_t
+_dbus_pending_call_set_data_unlocked (DBusPendingCall *pending,
+ dbus_int32_t slot,
+ void *data,
+ DBusFreeFunction free_data_func)
+{
+ DBusFreeFunction old_free_func;
+ void *old_data;
+ dbus_bool_t retval;
+
+ retval = _dbus_data_slot_list_set (&slot_allocator,
+ &pending->slot_list,
+ slot, data, free_data_func,
+ &old_free_func, &old_data);
+
+ /* Drop locks to call out to app code */
+ CONNECTION_UNLOCK (pending->connection);
+
+ if (retval)
+ {
+ if (old_free_func)
+ (* old_free_func) (old_data);
+ }
+
+ CONNECTION_LOCK (pending->connection);
+
+ return retval;
+}
+
+/** @} */
+
+/**
+ * @defgroup DBusPendingCall DBusPendingCall
+ * @ingroup DBus
+ * @brief Pending reply to a method call message
+ *
+ * A DBusPendingCall is an object representing an
+ * expected reply. A #DBusPendingCall can be created
+ * when you send a message that should have a reply.
+ *
+ * @{
+ */
+
+/**
+ * @def DBUS_TIMEOUT_INFINITE
+ *
+ * An integer constant representing an infinite timeout. This has the
+ * numeric value 0x7fffffff (the largest 32-bit signed integer).
+ *
+ * For source compatibility with D-Bus versions earlier than 1.4.12, use
+ * 0x7fffffff, or INT32_MAX (assuming your platform has it).
+ */
+
+/**
+ * @def DBUS_TIMEOUT_USE_DEFAULT
+ *
+ * An integer constant representing a request to use the default timeout.
+ * This has numeric value -1.
+ *
+ * For source compatibility with D-Bus versions earlier than 1.4.12, use a
+ * literal -1.
+ */
+
+/**
+ * @typedef DBusPendingCall
+ *
+ * Opaque data type representing a message pending.
+ */
+
+/**
+ * Increments the reference count on a pending call.
+ *
+ * @param pending the pending call object
+ * @returns the pending call object
+ */
+DBusPendingCall *
+dbus_pending_call_ref (DBusPendingCall *pending)
+{
+ dbus_int32_t old_refcount;
+
+ _dbus_return_val_if_fail (pending != NULL, NULL);
+
+ old_refcount = _dbus_atomic_inc (&pending->refcount);
+ _dbus_pending_call_trace_ref (pending, old_refcount, old_refcount + 1,
+ "ref");
+
+ return pending;
+}
+
+/**
+ * Decrements the reference count on a pending call,
+ * freeing it if the count reaches 0.
+ *
+ * @param pending the pending call object
+ */
+void
+dbus_pending_call_unref (DBusPendingCall *pending)
+{
+ dbus_int32_t old_refcount;
+
+ _dbus_return_if_fail (pending != NULL);
+
+ old_refcount = _dbus_atomic_dec (&pending->refcount);
+ _dbus_pending_call_trace_ref (pending, old_refcount, old_refcount - 1,
+ "unref");
+
+ if (old_refcount == 1)
+ _dbus_pending_call_last_unref(pending);
+}
+
+/**
+ * Sets a notification function to be called when the reply is
+ * received or the pending call times out.
+ *
+ * @param pending the pending call
+ * @param function notifier function
+ * @param user_data data to pass to notifier function
+ * @param free_user_data function to free the user data
+ * @returns #FALSE if not enough memory
+ */
+dbus_bool_t
+dbus_pending_call_set_notify (DBusPendingCall *pending,
+ DBusPendingCallNotifyFunction function,
+ void *user_data,
+ DBusFreeFunction free_user_data)
+{
+ dbus_bool_t ret = FALSE;
+
+ _dbus_return_val_if_fail (pending != NULL, FALSE);
+
+ CONNECTION_LOCK (pending->connection);
+
+ /* could invoke application code! */
+ if (!_dbus_pending_call_set_data_unlocked (pending, notify_user_data_slot,
+ user_data, free_user_data))
+ goto out;
+
+ pending->function = function;
+ ret = TRUE;
+
+out:
+ CONNECTION_UNLOCK (pending->connection);
+
+ return ret;
+}
+
+/**
+ * Cancels the pending call, such that any reply or error received
+ * will just be ignored. Drops the dbus library's internal reference
+ * to the #DBusPendingCall so will free the call if nobody else is
+ * holding a reference. However you usually get a reference from
+ * dbus_connection_send_with_reply() so probably your app owns a ref
+ * also.
+ *
+ * Note that canceling a pending call will <em>not</em> simulate a
+ * timed-out call; if a call times out, then a timeout error reply is
+ * received. If you cancel the call, no reply is received unless the
+ * the reply was already received before you canceled.
+ *
+ * @param pending the pending call
+ */
+void
+dbus_pending_call_cancel (DBusPendingCall *pending)
+{
+ _dbus_return_if_fail (pending != NULL);
+
+ _dbus_connection_remove_pending_call (pending->connection,
+ pending);
+}
+
+/**
+ * Checks whether the pending call has received a reply
+ * yet, or not.
+ *
+ * @param pending the pending call
+ * @returns #TRUE if a reply has been received
+ */
+dbus_bool_t
+dbus_pending_call_get_completed (DBusPendingCall *pending)
+{
+ dbus_bool_t completed;
+
+ _dbus_return_val_if_fail (pending != NULL, FALSE);
+
+ CONNECTION_LOCK (pending->connection);
+ completed = pending->completed;
+ CONNECTION_UNLOCK (pending->connection);
+
+ return completed;
+}
+
+/**
+ * Gets the reply, or returns #NULL if none has been received
+ * yet. Ownership of the reply message passes to the caller. This
+ * function can only be called once per pending call, since the reply
+ * message is tranferred to the caller.
+ *
+ * @param pending the pending call
+ * @returns the reply message or #NULL.
+ */
+DBusMessage*
+dbus_pending_call_steal_reply (DBusPendingCall *pending)
+{
+ DBusMessage *message;
+
+ _dbus_return_val_if_fail (pending != NULL, NULL);
+ _dbus_return_val_if_fail (pending->completed, NULL);
+ _dbus_return_val_if_fail (pending->reply != NULL, NULL);
+
+ CONNECTION_LOCK (pending->connection);
+
+ message = pending->reply;
+ pending->reply = NULL;
+
+ CONNECTION_UNLOCK (pending->connection);
+
+ _dbus_message_trace_ref (message, -1, -1, "dbus_pending_call_steal_reply");
+ return message;
+}
+
+/**
+ * Block until the pending call is completed. The blocking is as with
+ * dbus_connection_send_with_reply_and_block(); it does not enter the
+ * main loop or process other messages, it simply waits for the reply
+ * in question.
+ *
+ * If the pending call is already completed, this function returns
+ * immediately.
+ *
+ * @todo when you start blocking, the timeout is reset, but it should
+ * really only use time remaining since the pending call was created.
+ * This requires storing timestamps instead of intervals in the timeout
+ *
+ * @param pending the pending call
+ */
+void
+dbus_pending_call_block (DBusPendingCall *pending)
+{
+ _dbus_return_if_fail (pending != NULL);
+
+ _dbus_connection_block_pending_call (pending);
+}
+
+/**
+ * Allocates an integer ID to be used for storing application-specific
+ * data on any DBusPendingCall. The allocated ID may then be used
+ * with dbus_pending_call_set_data() and dbus_pending_call_get_data().
+ * The passed-in slot must be initialized to -1, and is filled in
+ * with the slot ID. If the passed-in slot is not -1, it's assumed
+ * to be already allocated, and its refcount is incremented.
+ *
+ * The allocated slot is global, i.e. all DBusPendingCall objects will
+ * have a slot with the given integer ID reserved.
+ *
+ * @param slot_p address of a global variable storing the slot
+ * @returns #FALSE on failure (no memory)
+ */
+dbus_bool_t
+dbus_pending_call_allocate_data_slot (dbus_int32_t *slot_p)
+{
+ _dbus_return_val_if_fail (slot_p != NULL, FALSE);
+
+ return _dbus_data_slot_allocator_alloc (&slot_allocator,
+ slot_p);
+}
+
+/**
+ * Deallocates a global ID for #DBusPendingCall data slots.
+ * dbus_pending_call_get_data() and dbus_pending_call_set_data() may
+ * no longer be used with this slot. Existing data stored on existing
+ * DBusPendingCall objects will be freed when the #DBusPendingCall is
+ * finalized, but may not be retrieved (and may only be replaced if
+ * someone else reallocates the slot). When the refcount on the
+ * passed-in slot reaches 0, it is set to -1.
+ *
+ * @param slot_p address storing the slot to deallocate
+ */
+void
+dbus_pending_call_free_data_slot (dbus_int32_t *slot_p)
+{
+ _dbus_return_if_fail (slot_p != NULL);
+ _dbus_return_if_fail (*slot_p >= 0);
+
+ _dbus_data_slot_allocator_free (&slot_allocator, slot_p);
+}
+
+/**
+ * Stores a pointer on a #DBusPendingCall, along
+ * with an optional function to be used for freeing
+ * the data when the data is set again, or when
+ * the pending call is finalized. The slot number
+ * must have been allocated with dbus_pending_call_allocate_data_slot().
+ *
+ * @param pending the pending_call
+ * @param slot the slot number
+ * @param data the data to store
+ * @param free_data_func finalizer function for the data
+ * @returns #TRUE if there was enough memory to store the data
+ */
+dbus_bool_t
+dbus_pending_call_set_data (DBusPendingCall *pending,
+ dbus_int32_t slot,
+ void *data,
+ DBusFreeFunction free_data_func)
+{
+ dbus_bool_t retval;
+
+ _dbus_return_val_if_fail (pending != NULL, FALSE);
+ _dbus_return_val_if_fail (slot >= 0, FALSE);
+
+
+ CONNECTION_LOCK (pending->connection);
+ retval = _dbus_pending_call_set_data_unlocked (pending, slot, data, free_data_func);
+ CONNECTION_UNLOCK (pending->connection);
+ return retval;
+}
+
+/**
+ * Retrieves data previously set with dbus_pending_call_set_data().
+ * The slot must still be allocated (must not have been freed).
+ *
+ * @param pending the pending_call
+ * @param slot the slot to get data from
+ * @returns the data, or #NULL if not found
+ */
+void*
+dbus_pending_call_get_data (DBusPendingCall *pending,
+ dbus_int32_t slot)
+{
+ void *res;
+
+ _dbus_return_val_if_fail (pending != NULL, NULL);
+
+ CONNECTION_LOCK (pending->connection);
+ res = _dbus_data_slot_list_get (&slot_allocator,
+ &pending->slot_list,
+ slot);
+ CONNECTION_UNLOCK (pending->connection);
+
+ return res;
+}
+
+/** @} */
diff --git a/src/3rdparty/libdbus/dbus/dbus-pending-call.h b/src/3rdparty/libdbus/dbus/dbus-pending-call.h
new file mode 100644
index 00000000..3539a1f2
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-pending-call.h
@@ -0,0 +1,100 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-pending-call.h Object representing a call in progress.
+ *
+ * Copyright (C) 2002, 2003 Red Hat Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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
+ *
+ */
+#if !defined (DBUS_INSIDE_DBUS_H) && !defined (DBUS_COMPILATION)
+#error "Only <dbus/dbus.h> can be included directly, this file may disappear or change contents."
+#endif
+
+#ifndef DBUS_PENDING_CALL_H
+#define DBUS_PENDING_CALL_H
+
+#include <dbus/dbus-macros.h>
+#include <dbus/dbus-types.h>
+#include <dbus/dbus-connection.h>
+
+DBUS_BEGIN_DECLS
+
+/**
+ * @addtogroup DBusPendingCall
+ * @{
+ */
+
+#define DBUS_TIMEOUT_INFINITE ((int) 0x7fffffff)
+#define DBUS_TIMEOUT_USE_DEFAULT (-1)
+
+DBUS_EXPORT
+DBusPendingCall* dbus_pending_call_ref (DBusPendingCall *pending);
+DBUS_EXPORT
+void dbus_pending_call_unref (DBusPendingCall *pending);
+DBUS_EXPORT
+dbus_bool_t dbus_pending_call_set_notify (DBusPendingCall *pending,
+ DBusPendingCallNotifyFunction function,
+ void *user_data,
+ DBusFreeFunction free_user_data);
+DBUS_EXPORT
+void dbus_pending_call_cancel (DBusPendingCall *pending);
+DBUS_EXPORT
+dbus_bool_t dbus_pending_call_get_completed (DBusPendingCall *pending);
+DBUS_EXPORT
+DBusMessage* dbus_pending_call_steal_reply (DBusPendingCall *pending);
+DBUS_EXPORT
+void dbus_pending_call_block (DBusPendingCall *pending);
+
+DBUS_EXPORT
+dbus_bool_t dbus_pending_call_allocate_data_slot (dbus_int32_t *slot_p);
+DBUS_EXPORT
+void dbus_pending_call_free_data_slot (dbus_int32_t *slot_p);
+DBUS_EXPORT
+dbus_bool_t dbus_pending_call_set_data (DBusPendingCall *pending,
+ dbus_int32_t slot,
+ void *data,
+ DBusFreeFunction free_data_func);
+DBUS_EXPORT
+void* dbus_pending_call_get_data (DBusPendingCall *pending,
+ dbus_int32_t slot);
+
+/**
+ * Clear a variable or struct member that contains a #DBusPendingCall.
+ * If it does not contain #NULL, the pending call that was previously
+ * there is unreferenced with dbus_pending_call_unref().
+ *
+ * This is very similar to dbus_clear_connection(): see that function
+ * for more details.
+ *
+ * @param pointer_to_pending_call A pointer to a variable or struct member.
+ * pointer_to_pending_call must not be #NULL, but *pointer_to_pending_call
+ * may be #NULL.
+ */
+static inline void
+dbus_clear_pending_call (DBusPendingCall **pointer_to_pending_call)
+{
+ _dbus_clear_pointer_impl (DBusPendingCall, pointer_to_pending_call,
+ dbus_pending_call_unref);
+}
+
+/** @} */
+
+DBUS_END_DECLS
+
+#endif /* DBUS_PENDING_CALL_H */
diff --git a/src/3rdparty/libdbus/dbus/dbus-pipe-unix.c b/src/3rdparty/libdbus/dbus/dbus-pipe-unix.c
new file mode 100644
index 00000000..314c7293
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-pipe-unix.c
@@ -0,0 +1,85 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-pipe-unix.c unix related pipe implementation
+ *
+ * Copyright (C) 2002, 2003, 2006 Red Hat, Inc.
+ * Copyright (C) 2003 CodeFactory AB
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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-protocol.h"
+#include "dbus-string.h"
+#include "dbus-internals.h"
+#include "dbus-pipe.h"
+#include "dbus-sysdeps-unix.h"
+
+#include <errno.h>
+
+/**
+ * write data to a pipe.
+ *
+ * @param pipe the pipe instance
+ * @param buffer the buffer to write data from
+ * @param start the first byte in the buffer to write
+ * @param len the number of bytes to try to write
+ * @param error error return
+ * @returns the number of bytes written or -1 on error
+ */
+int
+_dbus_pipe_write (DBusPipe *pipe,
+ const DBusString *buffer,
+ int start,
+ int len,
+ DBusError *error)
+{
+ int written;
+
+ written = _dbus_write (pipe->fd, buffer, start, len);
+ if (written < 0)
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "Writing to pipe: %s\n",
+ _dbus_strerror (errno));
+ }
+ return written;
+}
+
+/**
+ * close a pipe.
+ *
+ * @param pipe the pipe instance
+ * @param error return location for an error
+ * @returns -1 if error is set
+ */
+int
+_dbus_pipe_close (DBusPipe *pipe,
+ DBusError *error)
+{
+ if (!_dbus_close (pipe->fd, error))
+ {
+ return -1;
+ }
+ else
+ {
+ _dbus_pipe_invalidate (pipe);
+ return 0;
+ }
+}
diff --git a/src/3rdparty/libdbus/dbus/dbus-pipe-win.c b/src/3rdparty/libdbus/dbus/dbus-pipe-win.c
new file mode 100644
index 00000000..5667ad91
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-pipe-win.c
@@ -0,0 +1,92 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-pipe-win.c windows related pipe implementation
+ *
+ * Copyright (C) 2002, 2003, 2006 Red Hat, Inc.
+ * Copyright (C) 2003 CodeFactory AB
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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-protocol.h"
+#include "dbus-string.h"
+#include "dbus-internals.h"
+#include "dbus-pipe.h"
+
+#include <windows.h>
+#include <io.h>
+
+/**
+ * write data to a pipe.
+ *
+ * @param pipe the pipe instance
+ * @param buffer the buffer to write data from
+ * @param start the first byte in the buffer to write
+ * @param len the number of bytes to try to write
+ * @param error error return
+ * @returns the number of bytes written or -1 on error
+ */
+int
+_dbus_pipe_write (DBusPipe *pipe,
+ const DBusString *buffer,
+ int start,
+ int len,
+ DBusError *error)
+{
+ const char *buffer_c = _dbus_string_get_const_data (buffer);
+ int written;
+
+ written = _write (pipe->fd, buffer_c + start, len);
+
+ if (written >= 0)
+ return written;
+
+ dbus_set_error (error, _dbus_error_from_system_errno (),
+ "Writing to pipe: %s",
+ _dbus_strerror_from_errno ());
+ return -1;
+}
+
+/**
+ * close a pipe.
+ *
+ * @param pipe the pipe instance
+ * @param error return location for an error
+ * @returns #FALSE if error is set
+ */
+int
+_dbus_pipe_close (DBusPipe *pipe,
+ DBusError *error)
+{
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ if (_close (pipe->fd) != 0)
+ {
+ dbus_set_error (error, _dbus_error_from_system_errno (),
+ "Could not close pipe fd %d: %s", pipe->fd,
+ _dbus_strerror_from_errno ());
+ return -1;
+ }
+ else
+ {
+ _dbus_pipe_invalidate (pipe);
+ return 0;
+ }
+}
diff --git a/src/3rdparty/libdbus/dbus/dbus-pipe.c b/src/3rdparty/libdbus/dbus/dbus-pipe.c
new file mode 100644
index 00000000..f1fb19ff
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-pipe.c
@@ -0,0 +1,87 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-pipe.c pipe implementation (internal to D-Bus implementation)
+ *
+ * Copyright (C) 2002, 2003, 2006 Red Hat, Inc.
+ * Copyright (C) 2003 CodeFactory AB
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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-pipe.h"
+
+/*
+ * init a pipe instance.
+ *
+ * @param pipe the pipe
+ * @param fd the file descriptor to init from
+ */
+void
+_dbus_pipe_init (DBusPipe *pipe,
+ int fd)
+{
+ pipe->fd = fd;
+}
+
+/**
+ * init a pipe with stdout
+ *
+ * @param pipe the pipe
+ */
+void
+_dbus_pipe_init_stdout (DBusPipe *pipe)
+{
+ _dbus_pipe_init (pipe, 1);
+}
+
+/**
+ * check if a pipe is valid; pipes can be set invalid, similar to
+ * a -1 file descriptor.
+ *
+ * @param pipe the pipe instance
+ * @returns #FALSE if pipe is not valid
+ */
+dbus_bool_t
+_dbus_pipe_is_valid(DBusPipe *pipe)
+{
+ return pipe->fd >= 0;
+}
+
+/**
+ * Check if a pipe is stdout or stderr.
+ *
+ * @param pipe the pipe instance
+ * @returns #TRUE if pipe is one of the standard out/err channels
+ */
+dbus_bool_t
+_dbus_pipe_is_stdout_or_stderr (DBusPipe *pipe)
+{
+ return pipe->fd == 1 || pipe->fd == 2;
+}
+
+/**
+ * Initializes a pipe to an invalid value.
+ * @param pipe the pipe
+ */
+void
+_dbus_pipe_invalidate (DBusPipe *pipe)
+{
+ pipe->fd = -1;
+}
diff --git a/src/3rdparty/libdbus/dbus/dbus-pipe.h b/src/3rdparty/libdbus/dbus/dbus-pipe.h
new file mode 100644
index 00000000..1e579aa4
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-pipe.h
@@ -0,0 +1,66 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-sysdeps.h Wrappers around system/libc features (internal to D-Bus implementation)
+ *
+ * Copyright (C) 2002, 2003 Red Hat, Inc.
+ * Copyright (C) 2003 CodeFactory AB
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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
+ *
+ */
+
+#ifndef DBUS_PIPE_H
+#define DBUS_PIPE_H
+
+#include <stdint.h>
+
+#ifdef HAVE_INTTYPES_H
+#include <inttypes.h>
+#endif
+
+#include <dbus/dbus-types.h>
+#include <dbus/dbus-errors.h>
+#include <dbus/dbus-string.h>
+#include <dbus/dbus-sysdeps.h>
+
+struct DBusPipe {
+ int fd;
+};
+
+DBUS_PRIVATE_EXPORT
+void _dbus_pipe_init (DBusPipe *pipe,
+ int fd);
+DBUS_PRIVATE_EXPORT
+void _dbus_pipe_init_stdout (DBusPipe *pipe);
+DBUS_PRIVATE_EXPORT
+int _dbus_pipe_write (DBusPipe *pipe,
+ const DBusString *buffer,
+ int start,
+ int len,
+ DBusError *error);
+DBUS_PRIVATE_EXPORT
+int _dbus_pipe_close (DBusPipe *pipe,
+ DBusError *error);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_pipe_is_valid (DBusPipe *pipe);
+DBUS_PRIVATE_EXPORT
+void _dbus_pipe_invalidate (DBusPipe *pipe);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_pipe_is_stdout_or_stderr (DBusPipe *pipe);
+
+#endif
diff --git a/src/3rdparty/libdbus/dbus/dbus-protocol.h b/src/3rdparty/libdbus/dbus/dbus-protocol.h
new file mode 100644
index 00000000..33e39df3
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-protocol.h
@@ -0,0 +1,488 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-protocol.h D-Bus protocol constants
+ *
+ * Copyright (C) 2002, 2003 CodeFactory AB
+ * Copyright (C) 2004, 2005 Red Hat, Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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
+ *
+ */
+
+#ifndef DBUS_PROTOCOL_H
+#define DBUS_PROTOCOL_H
+
+/* Don't include anything in here from anywhere else. It's
+ * intended for use by any random library.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#if 0
+} /* avoids confusing emacs indentation */
+#endif
+#endif
+
+/* Normally docs are in .c files, but there isn't a .c file for this. */
+/**
+ * @defgroup DBusProtocol Protocol constants
+ * @ingroup DBus
+ *
+ * @brief Defines constants which are part of the D-Bus protocol
+ *
+ * This header is intended for use by any library, not only libdbus.
+ *
+ * @{
+ */
+
+
+/* Message byte order */
+#define DBUS_LITTLE_ENDIAN ('l') /**< Code marking LSB-first byte order in the wire protocol. */
+#define DBUS_BIG_ENDIAN ('B') /**< Code marking MSB-first byte order in the wire protocol. */
+
+/** Protocol version. */
+#define DBUS_MAJOR_PROTOCOL_VERSION 1
+
+/** Type code that is never equal to a legitimate type code */
+#define DBUS_TYPE_INVALID ((int) '\0')
+/** #DBUS_TYPE_INVALID as a string literal instead of a int literal */
+#define DBUS_TYPE_INVALID_AS_STRING "\0"
+
+/* Primitive types */
+/** Type code marking an 8-bit unsigned integer */
+#define DBUS_TYPE_BYTE ((int) 'y')
+/** #DBUS_TYPE_BYTE as a string literal instead of a int literal */
+#define DBUS_TYPE_BYTE_AS_STRING "y"
+/** Type code marking a boolean */
+#define DBUS_TYPE_BOOLEAN ((int) 'b')
+/** #DBUS_TYPE_BOOLEAN as a string literal instead of a int literal */
+#define DBUS_TYPE_BOOLEAN_AS_STRING "b"
+/** Type code marking a 16-bit signed integer */
+#define DBUS_TYPE_INT16 ((int) 'n')
+/** #DBUS_TYPE_INT16 as a string literal instead of a int literal */
+#define DBUS_TYPE_INT16_AS_STRING "n"
+/** Type code marking a 16-bit unsigned integer */
+#define DBUS_TYPE_UINT16 ((int) 'q')
+/** #DBUS_TYPE_UINT16 as a string literal instead of a int literal */
+#define DBUS_TYPE_UINT16_AS_STRING "q"
+/** Type code marking a 32-bit signed integer */
+#define DBUS_TYPE_INT32 ((int) 'i')
+/** #DBUS_TYPE_INT32 as a string literal instead of a int literal */
+#define DBUS_TYPE_INT32_AS_STRING "i"
+/** Type code marking a 32-bit unsigned integer */
+#define DBUS_TYPE_UINT32 ((int) 'u')
+/** #DBUS_TYPE_UINT32 as a string literal instead of a int literal */
+#define DBUS_TYPE_UINT32_AS_STRING "u"
+/** Type code marking a 64-bit signed integer */
+#define DBUS_TYPE_INT64 ((int) 'x')
+/** #DBUS_TYPE_INT64 as a string literal instead of a int literal */
+#define DBUS_TYPE_INT64_AS_STRING "x"
+/** Type code marking a 64-bit unsigned integer */
+#define DBUS_TYPE_UINT64 ((int) 't')
+/** #DBUS_TYPE_UINT64 as a string literal instead of a int literal */
+#define DBUS_TYPE_UINT64_AS_STRING "t"
+/** Type code marking an 8-byte double in IEEE 754 format */
+#define DBUS_TYPE_DOUBLE ((int) 'd')
+/** #DBUS_TYPE_DOUBLE as a string literal instead of a int literal */
+#define DBUS_TYPE_DOUBLE_AS_STRING "d"
+/** Type code marking a UTF-8 encoded, nul-terminated Unicode string */
+#define DBUS_TYPE_STRING ((int) 's')
+/** #DBUS_TYPE_STRING as a string literal instead of a int literal */
+#define DBUS_TYPE_STRING_AS_STRING "s"
+/** Type code marking a D-Bus object path */
+#define DBUS_TYPE_OBJECT_PATH ((int) 'o')
+/** #DBUS_TYPE_OBJECT_PATH as a string literal instead of a int literal */
+#define DBUS_TYPE_OBJECT_PATH_AS_STRING "o"
+/** Type code marking a D-Bus type signature */
+#define DBUS_TYPE_SIGNATURE ((int) 'g')
+/** #DBUS_TYPE_SIGNATURE as a string literal instead of a int literal */
+#define DBUS_TYPE_SIGNATURE_AS_STRING "g"
+/** Type code marking a unix file descriptor */
+#define DBUS_TYPE_UNIX_FD ((int) 'h')
+/** #DBUS_TYPE_UNIX_FD as a string literal instead of a int literal */
+#define DBUS_TYPE_UNIX_FD_AS_STRING "h"
+
+/* Compound types */
+/** Type code marking a D-Bus array type */
+#define DBUS_TYPE_ARRAY ((int) 'a')
+/** #DBUS_TYPE_ARRAY as a string literal instead of a int literal */
+#define DBUS_TYPE_ARRAY_AS_STRING "a"
+/** Type code marking a D-Bus variant type */
+#define DBUS_TYPE_VARIANT ((int) 'v')
+/** #DBUS_TYPE_VARIANT as a string literal instead of a int literal */
+#define DBUS_TYPE_VARIANT_AS_STRING "v"
+
+/** STRUCT and DICT_ENTRY are sort of special since their codes can't
+ * appear in a type string, instead
+ * DBUS_STRUCT_BEGIN_CHAR/DBUS_DICT_ENTRY_BEGIN_CHAR have to appear
+ */
+/** Type code used to represent a struct; however, this type code does not appear
+ * in type signatures, instead #DBUS_STRUCT_BEGIN_CHAR and #DBUS_STRUCT_END_CHAR will
+ * appear in a signature.
+ */
+#define DBUS_TYPE_STRUCT ((int) 'r')
+/** #DBUS_TYPE_STRUCT as a string literal instead of a int literal */
+#define DBUS_TYPE_STRUCT_AS_STRING "r"
+/** Type code used to represent a dict entry; however, this type code does not appear
+ * in type signatures, instead #DBUS_DICT_ENTRY_BEGIN_CHAR and #DBUS_DICT_ENTRY_END_CHAR will
+ * appear in a signature.
+ */
+#define DBUS_TYPE_DICT_ENTRY ((int) 'e')
+/** #DBUS_TYPE_DICT_ENTRY as a string literal instead of a int literal */
+#define DBUS_TYPE_DICT_ENTRY_AS_STRING "e"
+
+/** Does not include #DBUS_TYPE_INVALID, #DBUS_STRUCT_BEGIN_CHAR, #DBUS_STRUCT_END_CHAR,
+ * #DBUS_DICT_ENTRY_BEGIN_CHAR, or #DBUS_DICT_ENTRY_END_CHAR - i.e. it is the number of
+ * valid types, not the number of distinct characters that may appear in a type signature.
+ */
+#define DBUS_NUMBER_OF_TYPES (16)
+
+/* characters other than typecodes that appear in type signatures */
+
+/** Code marking the start of a struct type in a type signature */
+#define DBUS_STRUCT_BEGIN_CHAR ((int) '(')
+/** #DBUS_STRUCT_BEGIN_CHAR as a string literal instead of a int literal */
+#define DBUS_STRUCT_BEGIN_CHAR_AS_STRING "("
+/** Code marking the end of a struct type in a type signature */
+#define DBUS_STRUCT_END_CHAR ((int) ')')
+/** #DBUS_STRUCT_END_CHAR a string literal instead of a int literal */
+#define DBUS_STRUCT_END_CHAR_AS_STRING ")"
+/** Code marking the start of a dict entry type in a type signature */
+#define DBUS_DICT_ENTRY_BEGIN_CHAR ((int) '{')
+/** #DBUS_DICT_ENTRY_BEGIN_CHAR as a string literal instead of a int literal */
+#define DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING "{"
+/** Code marking the end of a dict entry type in a type signature */
+#define DBUS_DICT_ENTRY_END_CHAR ((int) '}')
+/** #DBUS_DICT_ENTRY_END_CHAR as a string literal instead of a int literal */
+#define DBUS_DICT_ENTRY_END_CHAR_AS_STRING "}"
+
+/** Max length in bytes of a bus name, interface, or member (not object
+ * path, paths are unlimited). This is limited because lots of stuff
+ * is O(n) in this number, plus it would be obnoxious to type in a
+ * paragraph-long method name so most likely something like that would
+ * be an exploit.
+ */
+#define DBUS_MAXIMUM_NAME_LENGTH 255
+
+/** This one is 255 so it fits in a byte */
+#define DBUS_MAXIMUM_SIGNATURE_LENGTH 255
+
+/** Max length of a match rule string; to keep people from hosing the
+ * daemon with some huge rule
+ */
+#define DBUS_MAXIMUM_MATCH_RULE_LENGTH 1024
+
+/** Max arg number you can match on in a match rule, e.g.
+ * arg0='hello' is OK, arg3489720987='hello' is not
+ */
+#define DBUS_MAXIMUM_MATCH_RULE_ARG_NUMBER 63
+
+/** Max length of a marshaled array in bytes (64M, 2^26) We use signed
+ * int for lengths so must be INT_MAX or less. We need something a
+ * bit smaller than INT_MAX because the array is inside a message with
+ * header info, etc. so an INT_MAX array wouldn't allow the message
+ * overhead. The 64M number is an attempt at a larger number than
+ * we'd reasonably ever use, but small enough that your bus would chew
+ * through it fairly quickly without locking up forever. If you have
+ * data that's likely to be larger than this, you should probably be
+ * sending it in multiple incremental messages anyhow.
+ */
+#define DBUS_MAXIMUM_ARRAY_LENGTH (67108864)
+/** Number of bits you need in an unsigned to store the max array size */
+#define DBUS_MAXIMUM_ARRAY_LENGTH_BITS 26
+
+/** The maximum total message size including header and body; similar
+ * rationale to max array size.
+ */
+#define DBUS_MAXIMUM_MESSAGE_LENGTH (DBUS_MAXIMUM_ARRAY_LENGTH * 2)
+/** Number of bits you need in an unsigned to store the max message size */
+#define DBUS_MAXIMUM_MESSAGE_LENGTH_BITS 27
+
+/** The maximum total number of unix fds in a message. Similar
+ * rationale as DBUS_MAXIMUM_MESSAGE_LENGTH. However we divide by four
+ * given that one fd is an int and hence at least 32 bits.
+ */
+#define DBUS_MAXIMUM_MESSAGE_UNIX_FDS (DBUS_MAXIMUM_MESSAGE_LENGTH/4)
+/** Number of bits you need in an unsigned to store the max message unix fds */
+#define DBUS_MAXIMUM_MESSAGE_UNIX_FDS_BITS (DBUS_MAXIMUM_MESSAGE_LENGTH_BITS-2)
+
+/** Depth of recursion in the type tree. This is automatically limited
+ * to DBUS_MAXIMUM_SIGNATURE_LENGTH since you could only have an array
+ * of array of array of ... that fit in the max signature. But that's
+ * probably a bit too large.
+ */
+#define DBUS_MAXIMUM_TYPE_RECURSION_DEPTH 32
+
+/* Types of message */
+
+/** This value is never a valid message type, see dbus_message_get_type() */
+#define DBUS_MESSAGE_TYPE_INVALID 0
+/** Message type of a method call message, see dbus_message_get_type() */
+#define DBUS_MESSAGE_TYPE_METHOD_CALL 1
+/** Message type of a method return message, see dbus_message_get_type() */
+#define DBUS_MESSAGE_TYPE_METHOD_RETURN 2
+/** Message type of an error reply message, see dbus_message_get_type() */
+#define DBUS_MESSAGE_TYPE_ERROR 3
+/** Message type of a signal message, see dbus_message_get_type() */
+#define DBUS_MESSAGE_TYPE_SIGNAL 4
+
+#define DBUS_NUM_MESSAGE_TYPES 5
+
+/* Header flags */
+
+/** If set, this flag means that the sender of a message does not care about getting
+ * a reply, so the recipient need not send one. See dbus_message_set_no_reply().
+ */
+#define DBUS_HEADER_FLAG_NO_REPLY_EXPECTED 0x1
+/**
+ * If set, this flag means that even if the message bus knows how to start an owner for
+ * the destination bus name (see dbus_message_set_destination()), it should not
+ * do so. If this flag is not set, the bus may launch a program to process the
+ * message.
+ */
+#define DBUS_HEADER_FLAG_NO_AUTO_START 0x2
+/**
+ * If set on a method call, this flag means that the caller is prepared to
+ * wait for interactive authorization.
+ */
+#define DBUS_HEADER_FLAG_ALLOW_INTERACTIVE_AUTHORIZATION 0x4
+
+/* Header fields */
+
+/** Not equal to any valid header field code */
+#define DBUS_HEADER_FIELD_INVALID 0
+/** Header field code for the path - the path is the object emitting a signal or the object receiving a method call.
+ * See dbus_message_set_path().
+ */
+#define DBUS_HEADER_FIELD_PATH 1
+/** Header field code for the interface containing a member (method or signal).
+ * See dbus_message_set_interface().
+ */
+#define DBUS_HEADER_FIELD_INTERFACE 2
+/** Header field code for a member (method or signal). See dbus_message_set_member(). */
+#define DBUS_HEADER_FIELD_MEMBER 3
+/** Header field code for an error name (found in #DBUS_MESSAGE_TYPE_ERROR messages).
+ * See dbus_message_set_error_name().
+ */
+#define DBUS_HEADER_FIELD_ERROR_NAME 4
+/** Header field code for a reply serial, used to match a #DBUS_MESSAGE_TYPE_METHOD_RETURN message with the
+ * message that it's a reply to. See dbus_message_set_reply_serial().
+ */
+#define DBUS_HEADER_FIELD_REPLY_SERIAL 5
+/**
+ * Header field code for the destination bus name of a message. See dbus_message_set_destination().
+ */
+#define DBUS_HEADER_FIELD_DESTINATION 6
+/**
+ * Header field code for the sender of a message; usually initialized by the message bus.
+ * See dbus_message_set_sender().
+ */
+#define DBUS_HEADER_FIELD_SENDER 7
+/**
+ * Header field code for the type signature of a message.
+ */
+#define DBUS_HEADER_FIELD_SIGNATURE 8
+/**
+ * Header field code for the number of unix file descriptors associated
+ * with this message.
+ */
+#define DBUS_HEADER_FIELD_UNIX_FDS 9
+/**
+ * Header field code for the container instance that sent this message.
+ */
+#define DBUS_HEADER_FIELD_CONTAINER_INSTANCE 10
+
+
+/**
+ * Value of the highest-numbered header field code, can be used to determine
+ * the size of an array indexed by header field code. Remember though
+ * that unknown codes must be ignored, so check for that before
+ * indexing the array.
+ */
+#define DBUS_HEADER_FIELD_LAST DBUS_HEADER_FIELD_CONTAINER_INSTANCE
+
+/** Header format is defined as a signature:
+ * byte byte order
+ * byte message type ID
+ * byte flags
+ * byte protocol version
+ * uint32 body length
+ * uint32 serial
+ * array of struct (byte,variant) (field name, value)
+ *
+ * The length of the header can be computed as the
+ * fixed size of the initial data, plus the length of
+ * the array at the end, plus padding to an 8-boundary.
+ */
+#define DBUS_HEADER_SIGNATURE \
+ DBUS_TYPE_BYTE_AS_STRING \
+ DBUS_TYPE_BYTE_AS_STRING \
+ DBUS_TYPE_BYTE_AS_STRING \
+ DBUS_TYPE_BYTE_AS_STRING \
+ DBUS_TYPE_UINT32_AS_STRING \
+ DBUS_TYPE_UINT32_AS_STRING \
+ DBUS_TYPE_ARRAY_AS_STRING \
+ DBUS_STRUCT_BEGIN_CHAR_AS_STRING \
+ DBUS_TYPE_BYTE_AS_STRING \
+ DBUS_TYPE_VARIANT_AS_STRING \
+ DBUS_STRUCT_END_CHAR_AS_STRING
+
+
+/**
+ * The smallest header size that can occur. (It won't be valid due to
+ * missing required header fields.) This is 4 bytes, two uint32, an
+ * array length. This isn't any kind of resource limit, just the
+ * necessary/logical outcome of the header signature.
+ */
+#define DBUS_MINIMUM_HEADER_SIZE 16
+
+/* Errors */
+/* WARNING these get autoconverted to an enum in dbus-glib.h. Thus,
+ * if you change the order it breaks the ABI. Keep them in order.
+ * Also, don't change the formatting since that will break the sed
+ * script.
+ */
+/** A generic error; "something went wrong" - see the error message for more. */
+#define DBUS_ERROR_FAILED "org.freedesktop.DBus.Error.Failed"
+/** There was not enough memory to complete an operation. */
+#define DBUS_ERROR_NO_MEMORY "org.freedesktop.DBus.Error.NoMemory"
+/** The bus doesn't know how to launch a service to supply the bus name you wanted. */
+#define DBUS_ERROR_SERVICE_UNKNOWN "org.freedesktop.DBus.Error.ServiceUnknown"
+/** The bus name you referenced doesn't exist (i.e. no application owns it). */
+#define DBUS_ERROR_NAME_HAS_NO_OWNER "org.freedesktop.DBus.Error.NameHasNoOwner"
+/** No reply to a message expecting one, usually means a timeout occurred. */
+#define DBUS_ERROR_NO_REPLY "org.freedesktop.DBus.Error.NoReply"
+/** Something went wrong reading or writing to a socket, for example. */
+#define DBUS_ERROR_IO_ERROR "org.freedesktop.DBus.Error.IOError"
+/** A D-Bus bus address was malformed. */
+#define DBUS_ERROR_BAD_ADDRESS "org.freedesktop.DBus.Error.BadAddress"
+/** Requested operation isn't supported (like ENOSYS on UNIX). */
+#define DBUS_ERROR_NOT_SUPPORTED "org.freedesktop.DBus.Error.NotSupported"
+/** Some limited resource is exhausted. */
+#define DBUS_ERROR_LIMITS_EXCEEDED "org.freedesktop.DBus.Error.LimitsExceeded"
+/** Security restrictions don't allow doing what you're trying to do. */
+#define DBUS_ERROR_ACCESS_DENIED "org.freedesktop.DBus.Error.AccessDenied"
+/** Authentication didn't work. */
+#define DBUS_ERROR_AUTH_FAILED "org.freedesktop.DBus.Error.AuthFailed"
+/** Unable to connect to server (probably caused by ECONNREFUSED on a socket). */
+#define DBUS_ERROR_NO_SERVER "org.freedesktop.DBus.Error.NoServer"
+/** Certain timeout errors, possibly ETIMEDOUT on a socket.
+ * Note that #DBUS_ERROR_NO_REPLY is used for message reply timeouts.
+ * @warning this is confusingly-named given that #DBUS_ERROR_TIMED_OUT also exists. We can't fix
+ * it for compatibility reasons so just be careful.
+ */
+#define DBUS_ERROR_TIMEOUT "org.freedesktop.DBus.Error.Timeout"
+/** No network access (probably ENETUNREACH on a socket). */
+#define DBUS_ERROR_NO_NETWORK "org.freedesktop.DBus.Error.NoNetwork"
+/** Can't bind a socket since its address is in use (i.e. EADDRINUSE). */
+#define DBUS_ERROR_ADDRESS_IN_USE "org.freedesktop.DBus.Error.AddressInUse"
+/** The connection is disconnected and you're trying to use it. */
+#define DBUS_ERROR_DISCONNECTED "org.freedesktop.DBus.Error.Disconnected"
+/** Invalid arguments passed to a method call. */
+#define DBUS_ERROR_INVALID_ARGS "org.freedesktop.DBus.Error.InvalidArgs"
+/** Missing file. */
+#define DBUS_ERROR_FILE_NOT_FOUND "org.freedesktop.DBus.Error.FileNotFound"
+/** Existing file and the operation you're using does not silently overwrite. */
+#define DBUS_ERROR_FILE_EXISTS "org.freedesktop.DBus.Error.FileExists"
+/** Method name you invoked isn't known by the object you invoked it on. */
+#define DBUS_ERROR_UNKNOWN_METHOD "org.freedesktop.DBus.Error.UnknownMethod"
+/** Object you invoked a method on isn't known. */
+#define DBUS_ERROR_UNKNOWN_OBJECT "org.freedesktop.DBus.Error.UnknownObject"
+/** Interface you invoked a method on isn't known by the object. */
+#define DBUS_ERROR_UNKNOWN_INTERFACE "org.freedesktop.DBus.Error.UnknownInterface"
+/** Property you tried to access isn't known by the object. */
+#define DBUS_ERROR_UNKNOWN_PROPERTY "org.freedesktop.DBus.Error.UnknownProperty"
+/** Property you tried to set is read-only. */
+#define DBUS_ERROR_PROPERTY_READ_ONLY "org.freedesktop.DBus.Error.PropertyReadOnly"
+/** Certain timeout errors, e.g. while starting a service.
+ * @warning this is confusingly-named given that #DBUS_ERROR_TIMEOUT also exists. We can't fix
+ * it for compatibility reasons so just be careful.
+ */
+#define DBUS_ERROR_TIMED_OUT "org.freedesktop.DBus.Error.TimedOut"
+/** Tried to remove or modify a match rule that didn't exist. */
+#define DBUS_ERROR_MATCH_RULE_NOT_FOUND "org.freedesktop.DBus.Error.MatchRuleNotFound"
+/** The match rule isn't syntactically valid. */
+#define DBUS_ERROR_MATCH_RULE_INVALID "org.freedesktop.DBus.Error.MatchRuleInvalid"
+/** While starting a new process, the exec() call failed. */
+#define DBUS_ERROR_SPAWN_EXEC_FAILED "org.freedesktop.DBus.Error.Spawn.ExecFailed"
+/** While starting a new process, the fork() call failed. */
+#define DBUS_ERROR_SPAWN_FORK_FAILED "org.freedesktop.DBus.Error.Spawn.ForkFailed"
+/** While starting a new process, the child exited with a status code. */
+#define DBUS_ERROR_SPAWN_CHILD_EXITED "org.freedesktop.DBus.Error.Spawn.ChildExited"
+/** While starting a new process, the child exited on a signal. */
+#define DBUS_ERROR_SPAWN_CHILD_SIGNALED "org.freedesktop.DBus.Error.Spawn.ChildSignaled"
+/** While starting a new process, something went wrong. */
+#define DBUS_ERROR_SPAWN_FAILED "org.freedesktop.DBus.Error.Spawn.Failed"
+/** We failed to setup the environment correctly. */
+#define DBUS_ERROR_SPAWN_SETUP_FAILED "org.freedesktop.DBus.Error.Spawn.FailedToSetup"
+/** We failed to setup the config parser correctly. */
+#define DBUS_ERROR_SPAWN_CONFIG_INVALID "org.freedesktop.DBus.Error.Spawn.ConfigInvalid"
+/** Bus name was not valid. */
+#define DBUS_ERROR_SPAWN_SERVICE_INVALID "org.freedesktop.DBus.Error.Spawn.ServiceNotValid"
+/** Service file not found in system-services directory. */
+#define DBUS_ERROR_SPAWN_SERVICE_NOT_FOUND "org.freedesktop.DBus.Error.Spawn.ServiceNotFound"
+/** Permissions are incorrect on the setuid helper. */
+#define DBUS_ERROR_SPAWN_PERMISSIONS_INVALID "org.freedesktop.DBus.Error.Spawn.PermissionsInvalid"
+/** Service file invalid (Name, User or Exec missing). */
+#define DBUS_ERROR_SPAWN_FILE_INVALID "org.freedesktop.DBus.Error.Spawn.FileInvalid"
+/** There was not enough memory to complete the operation. */
+#define DBUS_ERROR_SPAWN_NO_MEMORY "org.freedesktop.DBus.Error.Spawn.NoMemory"
+/** Tried to get a UNIX process ID and it wasn't available. */
+#define DBUS_ERROR_UNIX_PROCESS_ID_UNKNOWN "org.freedesktop.DBus.Error.UnixProcessIdUnknown"
+/** A type signature is not valid. */
+#define DBUS_ERROR_INVALID_SIGNATURE "org.freedesktop.DBus.Error.InvalidSignature"
+/** A file contains invalid syntax or is otherwise broken. */
+#define DBUS_ERROR_INVALID_FILE_CONTENT "org.freedesktop.DBus.Error.InvalidFileContent"
+/** Asked for SELinux security context and it wasn't available. */
+#define DBUS_ERROR_SELINUX_SECURITY_CONTEXT_UNKNOWN "org.freedesktop.DBus.Error.SELinuxSecurityContextUnknown"
+/** Asked for ADT audit data and it wasn't available. */
+#define DBUS_ERROR_ADT_AUDIT_DATA_UNKNOWN "org.freedesktop.DBus.Error.AdtAuditDataUnknown"
+/** There's already an object with the requested object path. */
+#define DBUS_ERROR_OBJECT_PATH_IN_USE "org.freedesktop.DBus.Error.ObjectPathInUse"
+/** The message meta data does not match the payload. e.g. expected
+ number of file descriptors were not sent over the socket this message was received on. */
+#define DBUS_ERROR_INCONSISTENT_MESSAGE "org.freedesktop.DBus.Error.InconsistentMessage"
+/** The message is not allowed without performing interactive authorization,
+ * but could have succeeded if an interactive authorization step was
+ * allowed. */
+#define DBUS_ERROR_INTERACTIVE_AUTHORIZATION_REQUIRED "org.freedesktop.DBus.Error.InteractiveAuthorizationRequired"
+/** The connection is not from a container, or the specified container instance
+ * does not exist. */
+#define DBUS_ERROR_NOT_CONTAINER "org.freedesktop.DBus.Error.NotContainer"
+
+/* XML introspection format */
+
+/** XML namespace of the introspection format version 1.0 */
+#define DBUS_INTROSPECT_1_0_XML_NAMESPACE "http://www.freedesktop.org/standards/dbus"
+/** XML public identifier of the introspection format version 1.0 */
+#define DBUS_INTROSPECT_1_0_XML_PUBLIC_IDENTIFIER "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
+/** XML system identifier of the introspection format version 1.0 */
+#define DBUS_INTROSPECT_1_0_XML_SYSTEM_IDENTIFIER "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"
+/** XML document type declaration of the introspection format version 1.0 */
+#define DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE "<!DOCTYPE node PUBLIC \"" DBUS_INTROSPECT_1_0_XML_PUBLIC_IDENTIFIER "\"\n\"" DBUS_INTROSPECT_1_0_XML_SYSTEM_IDENTIFIER "\">\n"
+
+/** @} */
+
+#ifdef __cplusplus
+#if 0
+{ /* avoids confusing emacs indentation */
+#endif
+}
+#endif
+
+#endif /* DBUS_PROTOCOL_H */
diff --git a/src/3rdparty/libdbus/dbus/dbus-resources.c b/src/3rdparty/libdbus/dbus/dbus-resources.c
new file mode 100644
index 00000000..7e1c3e55
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-resources.c
@@ -0,0 +1,342 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-resources.c Resource tracking/limits
+ *
+ * Copyright (C) 2003 Red Hat Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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/dbus-resources.h>
+#include <dbus/dbus-internals.h>
+
+/**
+ * @defgroup DBusResources Resource limits related code
+ * @ingroup DBusInternals
+ * @brief DBusCounter and other stuff related to resource limits
+ *
+ * Types and functions related to tracking resource limits,
+ * such as the maximum amount of memory/unix fds a connection can use
+ * for messages, etc.
+ */
+
+/**
+ * @defgroup DBusResourcesInternals Resource limits implementation details
+ * @ingroup DBusInternals
+ * @brief Resource limits implementation details
+ *
+ * Implementation details of resource limits code.
+ *
+ * @{
+ */
+
+/**
+ * @brief Internals of DBusCounter.
+ *
+ * DBusCounter internals. DBusCounter is an opaque object, it must be
+ * used via accessor functions.
+ */
+struct DBusCounter
+{
+ int refcount; /**< reference count */
+
+ long size_value; /**< current size counter value */
+ long unix_fd_value; /**< current unix fd counter value */
+
+#ifdef DBUS_ENABLE_STATS
+ long peak_size_value; /**< largest ever size counter value */
+ long peak_unix_fd_value; /**< largest ever unix fd counter value */
+#endif
+
+ long notify_size_guard_value; /**< call notify function when crossing this size value */
+ long notify_unix_fd_guard_value; /**< call notify function when crossing this unix fd value */
+
+ DBusCounterNotifyFunction notify_function; /**< notify function */
+ void *notify_data; /**< data for notify function */
+ dbus_bool_t notify_pending : 1; /**< TRUE if the guard value has been crossed */
+ DBusRMutex *mutex; /**< Lock on the entire DBusCounter */
+};
+
+/** @} */ /* end of resource limits internals docs */
+
+/**
+ * @addtogroup DBusResources
+ * @{
+ */
+
+/**
+ * Creates a new DBusCounter. DBusCounter is used
+ * to count usage of some resource such as memory.
+ *
+ * @returns new counter or #NULL on failure
+ */
+DBusCounter*
+_dbus_counter_new (void)
+{
+ DBusCounter *counter;
+
+ counter = dbus_new0 (DBusCounter, 1);
+ if (counter == NULL)
+ return NULL;
+
+ counter->refcount = 1;
+
+ _dbus_rmutex_new_at_location (&counter->mutex);
+ if (counter->mutex == NULL)
+ {
+ dbus_free (counter);
+ counter = NULL;
+ }
+
+ return counter;
+}
+
+/**
+ * Increments refcount of the counter
+ *
+ * @param counter the counter
+ * @returns the counter
+ */
+DBusCounter *
+_dbus_counter_ref (DBusCounter *counter)
+{
+ _dbus_rmutex_lock (counter->mutex);
+
+ _dbus_assert (counter->refcount > 0);
+
+ counter->refcount += 1;
+
+ _dbus_rmutex_unlock (counter->mutex);
+
+ return counter;
+}
+
+/**
+ * Decrements refcount of the counter and possibly
+ * finalizes the counter.
+ *
+ * @param counter the counter
+ */
+void
+_dbus_counter_unref (DBusCounter *counter)
+{
+ dbus_bool_t last_ref = FALSE;
+
+ _dbus_rmutex_lock (counter->mutex);
+
+ _dbus_assert (counter->refcount > 0);
+
+ counter->refcount -= 1;
+ last_ref = (counter->refcount == 0);
+
+ _dbus_rmutex_unlock (counter->mutex);
+
+ if (last_ref)
+ {
+ _dbus_rmutex_free_at_location (&counter->mutex);
+ dbus_free (counter);
+ }
+}
+
+/**
+ * Adjusts the value of the size counter by the given
+ * delta which may be positive or negative.
+ *
+ * This function may be called with locks held. After calling it, when
+ * any relevant locks are no longer held you must call _dbus_counter_notify().
+ *
+ * @param counter the counter
+ * @param delta value to add to the size counter's current value
+ */
+void
+_dbus_counter_adjust_size (DBusCounter *counter,
+ long delta)
+{
+ long old = 0;
+
+ _dbus_rmutex_lock (counter->mutex);
+
+ old = counter->size_value;
+
+ counter->size_value += delta;
+
+#ifdef DBUS_ENABLE_STATS
+ if (counter->peak_size_value < counter->size_value)
+ counter->peak_size_value = counter->size_value;
+#endif
+
+#if 0
+ _dbus_verbose ("Adjusting counter %ld by %ld = %ld\n",
+ old, delta, counter->size_value);
+#endif
+
+ if (counter->notify_function != NULL &&
+ ((old < counter->notify_size_guard_value &&
+ counter->size_value >= counter->notify_size_guard_value) ||
+ (old >= counter->notify_size_guard_value &&
+ counter->size_value < counter->notify_size_guard_value)))
+ counter->notify_pending = TRUE;
+
+ _dbus_rmutex_unlock (counter->mutex);
+}
+
+/**
+ * Calls the notify function from _dbus_counter_set_notify(),
+ * if that function has been specified and the counter has crossed the
+ * guard value (in either direction) since the last call to this function.
+ *
+ * This function must not be called with locks held, since it can call out
+ * to user code.
+ */
+void
+_dbus_counter_notify (DBusCounter *counter)
+{
+ DBusCounterNotifyFunction notify_function = NULL;
+ void *notify_data = NULL;
+
+ _dbus_rmutex_lock (counter->mutex);
+ if (counter->notify_pending)
+ {
+ counter->notify_pending = FALSE;
+ notify_function = counter->notify_function;
+ notify_data = counter->notify_data;
+ }
+ _dbus_rmutex_unlock (counter->mutex);
+
+ if (notify_function != NULL)
+ (* notify_function) (counter, notify_data);
+}
+
+/**
+ * Adjusts the value of the unix fd counter by the given
+ * delta which may be positive or negative.
+ *
+ * This function may be called with locks held. After calling it, when
+ * any relevant locks are no longer held you must call _dbus_counter_notify().
+ *
+ * @param counter the counter
+ * @param delta value to add to the unix fds counter's current value
+ */
+void
+_dbus_counter_adjust_unix_fd (DBusCounter *counter,
+ long delta)
+{
+ long old = 0;
+
+ _dbus_rmutex_lock (counter->mutex);
+
+ old = counter->unix_fd_value;
+
+ counter->unix_fd_value += delta;
+
+#ifdef DBUS_ENABLE_STATS
+ if (counter->peak_unix_fd_value < counter->unix_fd_value)
+ counter->peak_unix_fd_value = counter->unix_fd_value;
+#endif
+
+#if 0
+ _dbus_verbose ("Adjusting counter %ld by %ld = %ld\n",
+ old, delta, counter->unix_fd_value);
+#endif
+
+ if (counter->notify_function != NULL &&
+ ((old < counter->notify_unix_fd_guard_value &&
+ counter->unix_fd_value >= counter->notify_unix_fd_guard_value) ||
+ (old >= counter->notify_unix_fd_guard_value &&
+ counter->unix_fd_value < counter->notify_unix_fd_guard_value)))
+ counter->notify_pending = TRUE;
+
+ _dbus_rmutex_unlock (counter->mutex);
+}
+
+/**
+ * Gets the current value of the size counter.
+ *
+ * @param counter the counter
+ * @returns its current size value
+ */
+long
+_dbus_counter_get_size_value (DBusCounter *counter)
+{
+ long result;
+ _dbus_rmutex_lock (counter->mutex);
+ result = counter->size_value;
+ _dbus_rmutex_unlock (counter->mutex);
+ return result;
+}
+
+/**
+ * Gets the current value of the unix fd counter.
+ *
+ * @param counter the counter
+ * @returns its current unix fd value
+ */
+long
+_dbus_counter_get_unix_fd_value (DBusCounter *counter)
+{
+ long result;
+ _dbus_rmutex_lock (counter->mutex);
+ result = counter->unix_fd_value;
+ _dbus_rmutex_unlock (counter->mutex);
+ return result;
+}
+
+/**
+ * Sets the notify function for this counter; the notify function is
+ * called whenever the counter's values cross the guard values in
+ * either direction (moving up, or moving down).
+ *
+ * @param counter the counter
+ * @param size_guard_value the value we're notified if the size counter crosses
+ * @param unix_fd_guard_value the value we're notified if the unix fd counter crosses
+ * @param function function to call in order to notify
+ * @param user_data data to pass to the function
+ */
+void
+_dbus_counter_set_notify (DBusCounter *counter,
+ long size_guard_value,
+ long unix_fd_guard_value,
+ DBusCounterNotifyFunction function,
+ void *user_data)
+{
+ _dbus_rmutex_lock (counter->mutex);
+ counter->notify_size_guard_value = size_guard_value;
+ counter->notify_unix_fd_guard_value = unix_fd_guard_value;
+ counter->notify_function = function;
+ counter->notify_data = user_data;
+ counter->notify_pending = FALSE;
+ _dbus_rmutex_unlock (counter->mutex);
+}
+
+#ifdef DBUS_ENABLE_STATS
+long
+_dbus_counter_get_peak_size_value (DBusCounter *counter)
+{
+ return counter->peak_size_value;
+}
+
+long
+_dbus_counter_get_peak_unix_fd_value (DBusCounter *counter)
+{
+ return counter->peak_unix_fd_value;
+}
+#endif
+
+/** @} */ /* end of resource limits exported API */
diff --git a/src/3rdparty/libdbus/dbus/dbus-resources.h b/src/3rdparty/libdbus/dbus/dbus-resources.h
new file mode 100644
index 00000000..2df56322
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-resources.h
@@ -0,0 +1,69 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-resources.h Resource tracking/limits
+ *
+ * Copyright (C) 2003 Red Hat Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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
+ *
+ */
+#ifndef DBUS_RESOURCES_H
+#define DBUS_RESOURCES_H
+
+#include <dbus/dbus-macros.h>
+#include <dbus/dbus-macros-internal.h>
+#include <dbus/dbus-errors.h>
+#include <dbus/dbus-connection.h>
+
+DBUS_BEGIN_DECLS
+
+typedef struct DBusCounter DBusCounter;
+
+typedef void (* DBusCounterNotifyFunction) (DBusCounter *counter,
+ void *user_data);
+DBUS_EMBEDDED_TESTS_EXPORT
+DBusCounter* _dbus_counter_new (void);
+DBusCounter* _dbus_counter_ref (DBusCounter *counter);
+DBUS_EMBEDDED_TESTS_EXPORT
+void _dbus_counter_unref (DBusCounter *counter);
+
+DBUS_EMBEDDED_TESTS_EXPORT
+void _dbus_counter_adjust_size (DBusCounter *counter,
+ long delta);
+DBUS_EMBEDDED_TESTS_EXPORT
+void _dbus_counter_adjust_unix_fd (DBusCounter *counter,
+ long delta);
+void _dbus_counter_notify (DBusCounter *counter);
+DBUS_EMBEDDED_TESTS_EXPORT
+long _dbus_counter_get_size_value (DBusCounter *counter);
+DBUS_EMBEDDED_TESTS_EXPORT
+long _dbus_counter_get_unix_fd_value (DBusCounter *counter);
+
+void _dbus_counter_set_notify (DBusCounter *counter,
+ long size_guard_value,
+ long unix_fd_guard_value,
+ DBusCounterNotifyFunction function,
+ void *user_data);
+
+/* if DBUS_ENABLE_STATS */
+long _dbus_counter_get_peak_size_value (DBusCounter *counter);
+long _dbus_counter_get_peak_unix_fd_value (DBusCounter *counter);
+
+DBUS_END_DECLS
+
+#endif /* DBUS_RESOURCES_H */
diff --git a/src/3rdparty/libdbus/dbus/dbus-server-debug-pipe.c b/src/3rdparty/libdbus/dbus/dbus-server-debug-pipe.c
new file mode 100644
index 00000000..bf91939a
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-server-debug-pipe.c
@@ -0,0 +1,433 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-server-debug-pipe.c In-proc debug server implementation
+ *
+ * Copyright (C) 2003 CodeFactory AB
+ * Copyright (C) 2003, 2004 Red Hat, Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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-server-debug-pipe.h"
+#include "dbus-transport-socket.h"
+#include "dbus-connection-internal.h"
+#include "dbus-hash.h"
+#include "dbus-string.h"
+#include "dbus-protocol.h"
+
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
+
+/**
+ * @defgroup DBusServerDebugPipe DBusServerDebugPipe
+ * @ingroup DBusInternals
+ * @brief In-process pipe debug server used in unit tests.
+ *
+ * Types and functions related to DBusServerDebugPipe.
+ * This is used for unit testing.
+ *
+ * @{
+ */
+
+/**
+ * Opaque object representing a debug server implementation.
+ */
+typedef struct DBusServerDebugPipe DBusServerDebugPipe;
+
+/**
+ * Implementation details of DBusServerDebugPipe. All members
+ * are private.
+ */
+struct DBusServerDebugPipe
+{
+ DBusServer base; /**< Parent class members. */
+
+ char *name; /**< Server name. */
+
+ dbus_bool_t disconnected; /**< TRUE if disconnect has been called */
+};
+
+/* FIXME not threadsafe (right now the test suite doesn't use threads anyhow ) */
+static DBusHashTable *server_pipe_hash;
+static int server_pipe_hash_refcount = 0;
+
+static dbus_bool_t
+pipe_hash_ref (void)
+{
+ if (!server_pipe_hash)
+ {
+ _dbus_assert (server_pipe_hash_refcount == 0);
+
+ server_pipe_hash = _dbus_hash_table_new (DBUS_HASH_STRING, NULL, NULL);
+
+ if (!server_pipe_hash)
+ return FALSE;
+ }
+
+ server_pipe_hash_refcount = 1;
+
+ return TRUE;
+}
+
+static void
+pipe_hash_unref (void)
+{
+ _dbus_assert (server_pipe_hash != NULL);
+ _dbus_assert (server_pipe_hash_refcount > 0);
+
+ server_pipe_hash_refcount -= 1;
+ if (server_pipe_hash_refcount == 0)
+ {
+ _dbus_hash_table_unref (server_pipe_hash);
+ server_pipe_hash = NULL;
+ }
+}
+
+static void
+debug_finalize (DBusServer *server)
+{
+ DBusServerDebugPipe *debug_server = (DBusServerDebugPipe*) server;
+
+ pipe_hash_unref ();
+
+ _dbus_server_finalize_base (server);
+
+ dbus_free (debug_server->name);
+ dbus_free (server);
+}
+
+static void
+debug_disconnect (DBusServer *server)
+{
+ ((DBusServerDebugPipe*)server)->disconnected = TRUE;
+}
+
+static DBusServerVTable debug_vtable = {
+ debug_finalize,
+ debug_disconnect
+};
+
+/**
+ * Creates a new debug server using an in-process pipe
+ *
+ * @param server_name the name of the server.
+ * @param error address where an error can be returned.
+ * @returns a new server, or #NULL on failure.
+ */
+DBusServer*
+_dbus_server_debug_pipe_new (const char *server_name,
+ DBusError *error)
+{
+ DBusServerDebugPipe *debug_server;
+ DBusString address;
+ DBusString name_str;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ if (!pipe_hash_ref ())
+ return NULL;
+
+ if (_dbus_hash_table_lookup_string (server_pipe_hash, server_name) != NULL)
+ {
+ dbus_set_error (error, DBUS_ERROR_ADDRESS_IN_USE, NULL);
+ pipe_hash_unref ();
+ return NULL;
+ }
+
+ debug_server = dbus_new0 (DBusServerDebugPipe, 1);
+ if (debug_server == NULL)
+ goto nomem_0;
+
+ if (!_dbus_string_init (&address))
+ goto nomem_1;
+
+ _dbus_string_init_const (&name_str, server_name);
+ if (!_dbus_string_append (&address, "debug-pipe:name=") ||
+ !_dbus_address_append_escaped (&address, &name_str))
+ goto nomem_2;
+
+ debug_server->name = _dbus_strdup (server_name);
+ if (debug_server->name == NULL)
+ goto nomem_2;
+
+ if (!_dbus_server_init_base (&debug_server->base,
+ &debug_vtable, &address,
+ error))
+ goto fail_3;
+
+ if (!_dbus_hash_table_insert_string (server_pipe_hash,
+ debug_server->name,
+ debug_server))
+ goto nomem_4;
+
+ _dbus_string_free (&address);
+
+ /* server keeps the pipe hash ref */
+
+ _dbus_server_trace_ref (&debug_server->base, 0, 1, "debug_pipe_new");
+ return (DBusServer *)debug_server;
+
+ nomem_4:
+ _dbus_server_finalize_base (&debug_server->base);
+ fail_3:
+ dbus_free (debug_server->name);
+ nomem_2:
+ _dbus_string_free (&address);
+ nomem_1:
+ dbus_free (debug_server);
+ nomem_0:
+ pipe_hash_unref ();
+ if (error != NULL && !dbus_error_is_set (error))
+ _DBUS_SET_OOM (error);
+ return NULL;
+}
+
+/**
+ * Creates the client-side transport for
+ * a debug-pipe connection connected to the
+ * given debug-pipe server name.
+ *
+ * @param server_name name of server to connect to
+ * @param error address where an error can be returned.
+ * @returns #NULL on no memory or transport
+ */
+DBusTransport*
+_dbus_transport_debug_pipe_new (const char *server_name,
+ DBusError *error)
+{
+ DBusTransport *client_transport;
+ DBusTransport *server_transport;
+ DBusConnection *connection;
+ DBusSocket client_fd, server_fd;
+ DBusServer *server;
+ DBusString address;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ if (server_pipe_hash == NULL)
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_SERVER, NULL);
+ return NULL;
+ }
+
+ server = _dbus_hash_table_lookup_string (server_pipe_hash,
+ server_name);
+ if (server == NULL ||
+ ((DBusServerDebugPipe*)server)->disconnected)
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_SERVER, NULL);
+ return NULL;
+ }
+
+ if (!_dbus_string_init (&address))
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ return NULL;
+ }
+
+ if (!_dbus_string_append (&address, "debug-pipe:name=") ||
+ !_dbus_string_append (&address, server_name))
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ _dbus_string_free (&address);
+ return NULL;
+ }
+
+ if (!_dbus_socketpair (&client_fd, &server_fd, FALSE, NULL))
+ {
+ _dbus_verbose ("failed to create full duplex pipe\n");
+ dbus_set_error (error, DBUS_ERROR_FAILED, "Could not create full-duplex pipe");
+ _dbus_string_free (&address);
+ return NULL;
+ }
+
+ client_transport = _dbus_transport_new_for_socket (client_fd,
+ NULL, &address);
+ if (client_transport == NULL)
+ {
+ _dbus_close_socket (&client_fd, NULL);
+ _dbus_close_socket (&server_fd, NULL);
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ _dbus_string_free (&address);
+ return NULL;
+ }
+
+ _dbus_string_free (&address);
+
+ _dbus_socket_invalidate (&client_fd);
+
+ server_transport = _dbus_transport_new_for_socket (server_fd,
+ &server->guid_hex, NULL);
+ if (server_transport == NULL)
+ {
+ _dbus_transport_unref (client_transport);
+ _dbus_close_socket (&server_fd, NULL);
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ return NULL;
+ }
+
+ _dbus_socket_invalidate (&server_fd);
+
+ if (!_dbus_transport_set_auth_mechanisms (server_transport,
+ (const char**) server->auth_mechanisms))
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ _dbus_transport_unref (server_transport);
+ _dbus_transport_unref (client_transport);
+ return NULL;
+ }
+
+ connection = _dbus_connection_new_for_transport (server_transport);
+ _dbus_transport_unref (server_transport);
+ server_transport = NULL;
+
+ if (connection == NULL)
+ {
+ _dbus_transport_unref (client_transport);
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ return NULL;
+ }
+
+ /* See if someone wants to handle this new connection,
+ * self-referencing for paranoia
+ */
+ if (server->new_connection_function)
+ {
+ dbus_server_ref (server);
+ (* server->new_connection_function) (server, connection,
+ server->new_connection_data);
+ dbus_server_unref (server);
+ }
+
+ /* If no one grabbed a reference, the connection will die,
+ * and the client transport will get an immediate disconnect
+ */
+ _dbus_connection_close_if_only_one_ref (connection);
+ dbus_connection_unref (connection);
+
+ return client_transport;
+}
+
+/**
+ * Tries to interpret the address entry as a debug pipe entry.
+ *
+ * Sets error if the result is not OK.
+ *
+ * @param entry an address entry
+ * @param server_p location to store a new DBusServer, or #NULL on failure.
+ * @param error location to store rationale for failure on bad address
+ * @returns the outcome
+ *
+ */
+DBusServerListenResult
+_dbus_server_listen_debug_pipe (DBusAddressEntry *entry,
+ DBusServer **server_p,
+ DBusError *error)
+{
+ const char *method;
+
+ *server_p = NULL;
+
+ method = dbus_address_entry_get_method (entry);
+
+ if (strcmp (method, "debug-pipe") == 0)
+ {
+ const char *name = dbus_address_entry_get_value (entry, "name");
+
+ if (name == NULL)
+ {
+ _dbus_set_bad_address(error, "debug-pipe", "name",
+ NULL);
+ return DBUS_SERVER_LISTEN_BAD_ADDRESS;
+ }
+
+ *server_p = _dbus_server_debug_pipe_new (name, error);
+
+ if (*server_p)
+ {
+ _DBUS_ASSERT_ERROR_IS_CLEAR(error);
+ return DBUS_SERVER_LISTEN_OK;
+ }
+ else
+ {
+ _DBUS_ASSERT_ERROR_IS_SET(error);
+ return DBUS_SERVER_LISTEN_DID_NOT_CONNECT;
+ }
+ }
+ else
+ {
+ _DBUS_ASSERT_ERROR_IS_CLEAR(error);
+ return DBUS_SERVER_LISTEN_NOT_HANDLED;
+ }
+}
+
+/**
+ * Opens a debug pipe transport, used in the test suite.
+ *
+ * @param entry the address entry to try opening as debug-pipe
+ * @param transport_p return location for the opened transport
+ * @param error error to be set
+ * @returns result of the attempt
+ */
+DBusTransportOpenResult
+_dbus_transport_open_debug_pipe (DBusAddressEntry *entry,
+ DBusTransport **transport_p,
+ DBusError *error)
+{
+ const char *method;
+
+ method = dbus_address_entry_get_method (entry);
+ _dbus_assert (method != NULL);
+
+ if (strcmp (method, "debug-pipe") == 0)
+ {
+ const char *name = dbus_address_entry_get_value (entry, "name");
+
+ if (name == NULL)
+ {
+ _dbus_set_bad_address (error, "debug-pipe", "name",
+ NULL);
+ return DBUS_TRANSPORT_OPEN_BAD_ADDRESS;
+ }
+
+ *transport_p = _dbus_transport_debug_pipe_new (name, error);
+
+ if (*transport_p == NULL)
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ return DBUS_TRANSPORT_OPEN_DID_NOT_CONNECT;
+ }
+ else
+ {
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+ return DBUS_TRANSPORT_OPEN_OK;
+ }
+ }
+ else
+ {
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+ return DBUS_TRANSPORT_OPEN_NOT_HANDLED;
+ }
+}
+
+
+/** @} */
+
+#endif /* DBUS_ENABLE_EMBEDDED_TESTS */
diff --git a/src/3rdparty/libdbus/dbus/dbus-server-debug-pipe.h b/src/3rdparty/libdbus/dbus/dbus-server-debug-pipe.h
new file mode 100644
index 00000000..4284dc57
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-server-debug-pipe.h
@@ -0,0 +1,49 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-server-debug-pipe.h In-proc debug server implementation
+ *
+ * Copyright (C) 2003 CodeFactory AB
+ * Copyright (C) 2003 Red Hat, Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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
+ *
+ */
+#ifndef DBUS_SERVER_DEBUG_PIPE_H
+#define DBUS_SERVER_DEBUG_PIPE_H
+
+#include <dbus/dbus-internals.h>
+#include <dbus/dbus-server-protected.h>
+#include <dbus/dbus-transport-protected.h>
+
+DBUS_BEGIN_DECLS
+
+DBusServer* _dbus_server_debug_pipe_new (const char *server_name,
+ DBusError *error);
+DBusTransport* _dbus_transport_debug_pipe_new (const char *server_name,
+ DBusError *error);
+DBusServerListenResult _dbus_server_listen_debug_pipe (DBusAddressEntry *entry,
+ DBusServer **server_p,
+ DBusError *error);
+DBusTransportOpenResult _dbus_transport_open_debug_pipe (DBusAddressEntry *entry,
+ DBusTransport **transport_p,
+ DBusError *error);
+
+
+DBUS_END_DECLS
+
+#endif /* DBUS_SERVER_DEBUG_PIPE_H */
diff --git a/src/3rdparty/libdbus/dbus/dbus-server-protected.h b/src/3rdparty/libdbus/dbus/dbus-server-protected.h
new file mode 100644
index 00000000..d5aee150
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-server-protected.h
@@ -0,0 +1,186 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-server-protected.h Used by subclasses of DBusServer object (internal to D-Bus implementation)
+ *
+ * Copyright (C) 2002 Red Hat Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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
+ *
+ */
+#ifndef DBUS_SERVER_PROTECTED_H
+#define DBUS_SERVER_PROTECTED_H
+
+#include <dbus/dbus-internals.h>
+#include <dbus/dbus-threads-internal.h>
+#include <dbus/dbus-server.h>
+#include <dbus/dbus-address.h>
+#include <dbus/dbus-timeout.h>
+#include <dbus/dbus-watch.h>
+#include <dbus/dbus-resources.h>
+#include <dbus/dbus-dataslot.h>
+#include <dbus/dbus-string.h>
+
+DBUS_BEGIN_DECLS
+
+typedef struct DBusServerVTable DBusServerVTable;
+
+/**
+ * Virtual table to be implemented by all server "subclasses"
+ */
+struct DBusServerVTable
+{
+ void (* finalize) (DBusServer *server);
+ /**< The finalize method must free the server. */
+
+ void (* disconnect) (DBusServer *server);
+ /**< Disconnect this server. */
+};
+
+/**
+ * @ingroup DBusServerInternals
+ * Internals of DBusServer object
+ */
+struct DBusServer
+{
+ DBusAtomic refcount; /**< Reference count. */
+ const DBusServerVTable *vtable; /**< Virtual methods for this instance. */
+ DBusRMutex *mutex; /**< Lock on the server object */
+
+ DBusGUID guid; /**< Globally unique ID of server */
+
+ DBusString guid_hex; /**< Hex-encoded version of GUID */
+
+ DBusWatchList *watches; /**< Our watches */
+ DBusTimeoutList *timeouts; /**< Our timeouts */
+
+ char *address; /**< Address this server is listening on. */
+ dbus_bool_t published_address; /**< flag which indicates that server has published its bus address. */
+
+ int max_connections; /**< Max number of connections allowed at once. */
+
+ DBusDataSlotList slot_list; /**< Data stored by allocated integer ID */
+
+ DBusNewConnectionFunction new_connection_function;
+ /**< Callback to invoke when a new connection is created. */
+ void *new_connection_data;
+ /**< Data for new connection callback */
+ DBusFreeFunction new_connection_free_data_function;
+ /**< Callback to invoke to free new_connection_data
+ * when server is finalized or data is replaced.
+ */
+
+ char **auth_mechanisms; /**< Array of allowed authentication mechanisms */
+
+ unsigned int disconnected : 1; /**< TRUE if we are disconnected. */
+
+#ifndef DBUS_DISABLE_CHECKS
+ unsigned int have_server_lock : 1; /**< Does someone have the server mutex locked */
+#endif
+};
+
+dbus_bool_t _dbus_server_init_base (DBusServer *server,
+ const DBusServerVTable *vtable,
+ const DBusString *address,
+ DBusError *error);
+void _dbus_server_finalize_base (DBusServer *server);
+void _dbus_server_disconnect_unlocked (DBusServer *server);
+dbus_bool_t _dbus_server_add_watch (DBusServer *server,
+ DBusWatch *watch);
+void _dbus_server_remove_watch (DBusServer *server,
+ DBusWatch *watch);
+DBUS_PRIVATE_EXPORT
+void _dbus_server_toggle_all_watches (DBusServer *server,
+ dbus_bool_t enabled);
+dbus_bool_t _dbus_server_add_timeout (DBusServer *server,
+ DBusTimeout *timeout);
+void _dbus_server_remove_timeout (DBusServer *server,
+ DBusTimeout *timeout);
+void _dbus_server_toggle_timeout (DBusServer *server,
+ DBusTimeout *timeout,
+ dbus_bool_t enabled);
+
+DBUS_PRIVATE_EXPORT
+void _dbus_server_ref_unlocked (DBusServer *server);
+DBUS_PRIVATE_EXPORT
+void _dbus_server_unref_unlocked (DBusServer *server);
+
+typedef enum
+{
+ DBUS_SERVER_LISTEN_NOT_HANDLED, /**< we aren't in charge of this address type */
+ DBUS_SERVER_LISTEN_OK, /**< we set up the listen */
+ DBUS_SERVER_LISTEN_BAD_ADDRESS, /**< malformed address */
+ DBUS_SERVER_LISTEN_DID_NOT_CONNECT, /**< well-formed address but failed to set it up */
+ DBUS_SERVER_LISTEN_ADDRESS_ALREADY_USED /**< address is already used */
+} DBusServerListenResult;
+
+DBusServerListenResult _dbus_server_listen_unix_socket (DBusAddressEntry *entry,
+ DBusServer **server_p,
+ DBusError *error);
+
+DBusServerListenResult _dbus_server_listen_platform_specific (DBusAddressEntry *entry,
+ DBusServer **server_p,
+ DBusError *error);
+
+#ifdef DBUS_ENABLE_VERBOSE_MODE
+void _dbus_server_trace_ref (DBusServer *server,
+ int old_refcount,
+ int new_refcount,
+ const char *why);
+#else
+#define _dbus_server_trace_ref(s,o,n,w) \
+ do \
+ {\
+ (void) (o); \
+ (void) (n); \
+ } while (0)
+#endif
+
+#ifdef DBUS_DISABLE_CHECKS
+#define TOOK_LOCK_CHECK(server)
+#define RELEASING_LOCK_CHECK(server)
+#define HAVE_LOCK_CHECK(server)
+#else
+#define TOOK_LOCK_CHECK(server) do { \
+ _dbus_assert (!(server)->have_server_lock); \
+ (server)->have_server_lock = TRUE; \
+ } while (0)
+#define RELEASING_LOCK_CHECK(server) do { \
+ _dbus_assert ((server)->have_server_lock); \
+ (server)->have_server_lock = FALSE; \
+ } while (0)
+#define HAVE_LOCK_CHECK(server) _dbus_assert ((server)->have_server_lock)
+/* A "DO_NOT_HAVE_LOCK_CHECK" is impossible since we need the lock to check the flag */
+#endif
+
+#define TRACE_LOCKS 0
+
+#define SERVER_LOCK(server) do { \
+ if (TRACE_LOCKS) { _dbus_verbose ("LOCK\n"); } \
+ _dbus_rmutex_lock ((server)->mutex); \
+ TOOK_LOCK_CHECK (server); \
+ } while (0)
+
+#define SERVER_UNLOCK(server) do { \
+ if (TRACE_LOCKS) { _dbus_verbose ("UNLOCK\n"); } \
+ RELEASING_LOCK_CHECK (server); \
+ _dbus_rmutex_unlock ((server)->mutex); \
+ } while (0)
+
+DBUS_END_DECLS
+
+#endif /* DBUS_SERVER_PROTECTED_H */
diff --git a/src/3rdparty/libdbus/dbus/dbus-server-socket.c b/src/3rdparty/libdbus/dbus/dbus-server-socket.c
new file mode 100644
index 00000000..5dc1b54e
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-server-socket.c
@@ -0,0 +1,884 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-server-socket.c Server implementation for sockets
+ *
+ * Copyright (C) 2002, 2003, 2004, 2006 Red Hat Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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-server-socket.h"
+#include "dbus-transport-socket.h"
+#include "dbus-connection-internal.h"
+#include "dbus-memory.h"
+#include "dbus-nonce.h"
+#include "dbus-string.h"
+
+/**
+ * @defgroup DBusServerSocket DBusServer implementations for SOCKET
+ * @ingroup DBusInternals
+ * @brief Implementation details of DBusServer on SOCKET
+ *
+ * @{
+ */
+/**
+ *
+ * Opaque object representing a Socket server implementation.
+ */
+typedef struct DBusServerSocket DBusServerSocket;
+
+/**
+ * Implementation details of DBusServerSocket. All members
+ * are private.
+ */
+struct DBusServerSocket
+{
+ DBusServer base; /**< Parent class members. */
+ int n_fds; /**< Number of active file handles */
+ DBusSocket *fds; /**< File descriptor or DBUS_SOCKET_INVALID if disconnected. */
+ DBusWatch **watch; /**< File descriptor watch. */
+ char *socket_name; /**< Name of domain socket, to unlink if appropriate */
+ DBusNonceFile *noncefile; /**< Nonce file used to authenticate clients */
+};
+
+static void
+socket_finalize (DBusServer *server)
+{
+ DBusServerSocket *socket_server = (DBusServerSocket*) server;
+ int i;
+
+ _dbus_server_finalize_base (server);
+
+ for (i = 0 ; i < socket_server->n_fds ; i++)
+ if (socket_server->watch[i])
+ {
+ _dbus_watch_unref (socket_server->watch[i]);
+ socket_server->watch[i] = NULL;
+ }
+
+ dbus_free (socket_server->fds);
+ dbus_free (socket_server->watch);
+ dbus_free (socket_server->socket_name);
+ _dbus_noncefile_delete (&socket_server->noncefile, NULL);
+ dbus_free (server);
+}
+
+/* Return value is just for memory, not other failures. */
+static dbus_bool_t
+handle_new_client_fd_and_unlock (DBusServer *server,
+ DBusSocket client_fd)
+{
+ DBusConnection *connection;
+ DBusTransport *transport;
+ DBusNewConnectionFunction new_connection_function;
+ void *new_connection_data;
+
+ _dbus_verbose ("Creating new client connection with fd %" DBUS_SOCKET_FORMAT "\n",
+ _dbus_socket_printable (client_fd));
+
+ HAVE_LOCK_CHECK (server);
+
+ if (!_dbus_set_socket_nonblocking (client_fd, NULL))
+ {
+ SERVER_UNLOCK (server);
+ return TRUE;
+ }
+
+ transport = _dbus_transport_new_for_socket (client_fd, &server->guid_hex, NULL);
+ if (transport == NULL)
+ {
+ _dbus_close_socket (&client_fd, NULL);
+ SERVER_UNLOCK (server);
+ return FALSE;
+ }
+
+ if (!_dbus_transport_set_auth_mechanisms (transport,
+ (const char **) server->auth_mechanisms))
+ {
+ _dbus_transport_unref (transport);
+ SERVER_UNLOCK (server);
+ return FALSE;
+ }
+
+ /* note that client_fd is now owned by the transport, and will be
+ * closed on transport disconnection/finalization
+ */
+
+ connection = _dbus_connection_new_for_transport (transport);
+ _dbus_transport_unref (transport);
+ transport = NULL; /* now under the connection lock */
+
+ if (connection == NULL)
+ {
+ SERVER_UNLOCK (server);
+ return FALSE;
+ }
+
+ /* See if someone wants to handle this new connection, self-referencing
+ * for paranoia.
+ */
+ new_connection_function = server->new_connection_function;
+ new_connection_data = server->new_connection_data;
+
+ _dbus_server_ref_unlocked (server);
+ SERVER_UNLOCK (server);
+
+ if (new_connection_function)
+ {
+ (* new_connection_function) (server, connection,
+ new_connection_data);
+ }
+ dbus_server_unref (server);
+
+ /* If no one grabbed a reference, the connection will die. */
+ _dbus_connection_close_if_only_one_ref (connection);
+ dbus_connection_unref (connection);
+
+ return TRUE;
+}
+
+static dbus_bool_t
+socket_handle_watch (DBusWatch *watch,
+ unsigned int flags,
+ void *data)
+{
+ DBusServer *server = data;
+ DBusServerSocket *socket_server = data;
+
+#ifndef DBUS_DISABLE_ASSERT
+ int i;
+ dbus_bool_t found = FALSE;
+#endif
+
+ SERVER_LOCK (server);
+
+#ifndef DBUS_DISABLE_ASSERT
+ for (i = 0 ; i < socket_server->n_fds ; i++)
+ {
+ if (socket_server->watch[i] == watch)
+ {
+ found = TRUE;
+ break;
+ }
+ }
+ _dbus_assert (found);
+#endif
+
+ _dbus_verbose ("Handling client connection, flags 0x%x\n", flags);
+
+ if (flags & DBUS_WATCH_READABLE)
+ {
+ DBusSocket client_fd;
+ DBusSocket listen_fd;
+ int saved_errno;
+
+ listen_fd = _dbus_watch_get_socket (watch);
+
+ if (socket_server->noncefile)
+ client_fd = _dbus_accept_with_noncefile (listen_fd, socket_server->noncefile);
+ else
+ client_fd = _dbus_accept (listen_fd);
+
+ saved_errno = _dbus_save_socket_errno ();
+
+ if (!_dbus_socket_is_valid (client_fd))
+ {
+ /* EINTR handled for us */
+
+ if (_dbus_get_is_errno_eagain_or_ewouldblock (saved_errno))
+ _dbus_verbose ("No client available to accept after all\n");
+ else
+ _dbus_verbose ("Failed to accept a client connection: %s\n",
+ _dbus_strerror (saved_errno));
+
+ SERVER_UNLOCK (server);
+ }
+ else
+ {
+ if (!handle_new_client_fd_and_unlock (server, client_fd))
+ _dbus_verbose ("Rejected client connection due to lack of memory\n");
+ }
+ }
+
+ if (flags & DBUS_WATCH_ERROR)
+ _dbus_verbose ("Error on server listening socket\n");
+
+ if (flags & DBUS_WATCH_HANGUP)
+ _dbus_verbose ("Hangup on server listening socket\n");
+
+ return TRUE;
+}
+
+static void
+socket_disconnect (DBusServer *server)
+{
+ DBusServerSocket *socket_server = (DBusServerSocket*) server;
+ int i;
+
+ HAVE_LOCK_CHECK (server);
+
+ for (i = 0 ; i < socket_server->n_fds ; i++)
+ {
+ if (socket_server->watch[i])
+ {
+ _dbus_server_remove_watch (server,
+ socket_server->watch[i]);
+ _dbus_watch_invalidate (socket_server->watch[i]);
+ _dbus_watch_unref (socket_server->watch[i]);
+ socket_server->watch[i] = NULL;
+ }
+
+ if (_dbus_socket_is_valid (socket_server->fds[i]))
+ _dbus_close_socket (&socket_server->fds[i], NULL);
+ }
+
+ if (socket_server->socket_name != NULL)
+ {
+ DBusString tmp;
+ _dbus_string_init_const (&tmp, socket_server->socket_name);
+ _dbus_delete_file (&tmp, NULL);
+ }
+
+ if (server->published_address)
+ _dbus_daemon_unpublish_session_bus_address();
+
+ HAVE_LOCK_CHECK (server);
+}
+
+static const DBusServerVTable socket_vtable = {
+ socket_finalize,
+ socket_disconnect
+};
+
+/**
+ * Creates a new server listening on the given file descriptor. The
+ * file descriptor should be nonblocking (use
+ * _dbus_set_fd_nonblocking() to make it so). The file descriptor
+ * should be listening for connections, that is, listen() should have
+ * been successfully invoked on it. The server will use accept() to
+ * accept new client connections.
+ *
+ * @param fds list of file descriptors.
+ * @param n_fds number of file descriptors
+ * @param address the server's address
+ * @param noncefile to be used for authentication (NULL if not needed)
+ * @param error location to store reason for failure
+ * @returns the new server, or #NULL on OOM or other error.
+ *
+ */
+DBusServer*
+_dbus_server_new_for_socket (DBusSocket *fds,
+ int n_fds,
+ const DBusString *address,
+ DBusNonceFile *noncefile,
+ DBusError *error)
+{
+ DBusServerSocket *socket_server;
+ DBusServer *server;
+ int i;
+
+ socket_server = dbus_new0 (DBusServerSocket, 1);
+ if (socket_server == NULL)
+ goto failed;
+
+ socket_server->noncefile = noncefile;
+
+ socket_server->fds = dbus_new (DBusSocket, n_fds);
+ if (!socket_server->fds)
+ goto failed;
+
+ socket_server->watch = dbus_new0 (DBusWatch *, n_fds);
+ if (!socket_server->watch)
+ goto failed;
+
+ for (i = 0 ; i < n_fds ; i++)
+ {
+ DBusWatch *watch;
+
+ watch = _dbus_watch_new (_dbus_socket_get_pollable (fds[i]),
+ DBUS_WATCH_READABLE,
+ TRUE,
+ socket_handle_watch, socket_server,
+ NULL);
+ if (watch == NULL)
+ goto failed;
+
+ socket_server->n_fds++;
+ socket_server->fds[i] = fds[i];
+ socket_server->watch[i] = watch;
+ }
+
+ if (!_dbus_server_init_base (&socket_server->base,
+ &socket_vtable, address,
+ error))
+ goto failed;
+
+ server = (DBusServer*)socket_server;
+
+ SERVER_LOCK (server);
+
+ for (i = 0 ; i < n_fds ; i++)
+ {
+ if (!_dbus_server_add_watch (&socket_server->base,
+ socket_server->watch[i]))
+ {
+ int j;
+
+ /* The caller is still responsible for closing the fds until
+ * we return successfully, so don't let socket_disconnect()
+ * close them */
+ for (j = 0; j < n_fds; j++)
+ _dbus_socket_invalidate (&socket_server->fds[j]);
+
+ /* socket_disconnect() will try to remove all the watches;
+ * make sure it doesn't see the ones that weren't even added
+ * yet */
+ for (j = i; j < n_fds; j++)
+ {
+ _dbus_watch_invalidate (socket_server->watch[j]);
+ _dbus_watch_unref (socket_server->watch[j]);
+ socket_server->watch[j] = NULL;
+ }
+
+ _dbus_server_disconnect_unlocked (server);
+ SERVER_UNLOCK (server);
+ _dbus_server_finalize_base (&socket_server->base);
+ goto failed;
+ }
+ }
+
+ SERVER_UNLOCK (server);
+
+ _dbus_server_trace_ref (&socket_server->base, 0, 1, "new_for_socket");
+ return (DBusServer*) socket_server;
+
+failed:
+ if (socket_server != NULL)
+ {
+ if (socket_server->watch != NULL)
+ {
+ for (i = 0; i < n_fds; i++)
+ {
+ if (socket_server->watch[i] != NULL)
+ {
+ _dbus_watch_invalidate (socket_server->watch[i]);
+ _dbus_watch_unref (socket_server->watch[i]);
+ socket_server->watch[i] = NULL;
+ }
+ }
+ }
+
+ dbus_free (socket_server->watch);
+ dbus_free (socket_server->fds);
+ dbus_free (socket_server);
+ }
+
+ if (error != NULL && !dbus_error_is_set (error))
+ _DBUS_SET_OOM (error);
+
+ return NULL;
+}
+
+/**
+ * Creates a new server listening on TCP.
+ * If host is NULL, it will default to localhost.
+ * If bind is NULL, it will default to the value for the host
+ * parameter, and if that is NULL, then localhost
+ * If bind is a hostname, it will be resolved and will listen
+ * on all returned addresses.
+ * If family is NULL, hostname resolution will try all address
+ * families, otherwise it can be ipv4 or ipv6 to restrict the
+ * addresses considered.
+ *
+ * @param host the hostname to report for the listen address
+ * @param bind the hostname to listen on
+ * @param port the port to listen on or 0 to let the OS choose
+ * @param family
+ * @param error location to store reason for failure.
+ * @param use_nonce whether to use a nonce for low-level authentication (nonce-tcp transport) or not (tcp transport)
+ * @returns the new server, or #NULL on failure.
+ */
+DBusServer*
+_dbus_server_new_for_tcp_socket (const char *host,
+ const char *bind,
+ const char *port,
+ const char *family,
+ DBusError *error,
+ dbus_bool_t use_nonce)
+{
+ DBusServer *server = NULL;
+ DBusSocket *listen_fds = NULL;
+ int nlisten_fds = 0, i;
+ DBusString address = _DBUS_STRING_INIT_INVALID;
+ DBusString host_str; /* Initialized as const later, not freed */
+ DBusString port_str = _DBUS_STRING_INIT_INVALID;
+ DBusNonceFile *noncefile = NULL;
+ const char *family_used = NULL;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ if (!_dbus_string_init (&address))
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ goto failed;
+ }
+
+ if (!_dbus_string_init (&port_str))
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ goto failed;
+ }
+
+ if (host == NULL)
+ host = "localhost";
+
+ if (port == NULL)
+ port = "0";
+
+ if (bind == NULL)
+ bind = host;
+ else if (strcmp (bind, "*") == 0)
+ bind = NULL;
+
+ nlisten_fds =_dbus_listen_tcp_socket (bind, port, family,
+ &port_str,
+ &family_used,
+ &listen_fds, error);
+ if (nlisten_fds <= 0)
+ {
+ _DBUS_ASSERT_ERROR_IS_SET(error);
+ goto failed;
+ }
+
+ _dbus_string_init_const (&host_str, host);
+ if (!_dbus_string_append (&address, use_nonce ? "nonce-tcp:host=" : "tcp:host=") ||
+ !_dbus_address_append_escaped (&address, &host_str) ||
+ !_dbus_string_append (&address, ",port=") ||
+ !_dbus_string_append (&address, _dbus_string_get_const_data(&port_str)))
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ goto failed;
+ }
+ if (family_used &&
+ (!_dbus_string_append (&address, ",family=") ||
+ !_dbus_string_append (&address, family_used)))
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ goto failed;
+ }
+
+ if (use_nonce)
+ {
+ if (!_dbus_noncefile_create (&noncefile, error))
+ goto failed;
+
+ if (!_dbus_string_append (&address, ",noncefile=") ||
+ !_dbus_address_append_escaped (&address, _dbus_noncefile_get_path (noncefile)))
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ goto failed;
+ }
+ }
+
+ server = _dbus_server_new_for_socket (listen_fds, nlisten_fds, &address, noncefile, error);
+ if (server == NULL)
+ goto failed;
+
+ /* server has taken ownership of noncefile and the fds in listen_fds */
+ _dbus_string_free (&port_str);
+ _dbus_string_free (&address);
+ dbus_free(listen_fds);
+
+ return server;
+
+failed:
+ _dbus_noncefile_delete (&noncefile, NULL);
+
+ if (listen_fds != NULL)
+ {
+ for (i = 0; i < nlisten_fds; i++)
+ _dbus_close_socket (&listen_fds[i], NULL);
+ dbus_free (listen_fds);
+ }
+
+ _dbus_string_free (&port_str);
+ _dbus_string_free (&address);
+ return NULL;
+}
+
+/**
+ * Tries to interpret the address entry for various socket-related
+ * addresses (well, currently only tcp and nonce-tcp).
+ *
+ * Sets error if the result is not OK.
+ *
+ * @param entry an address entry
+ * @param server_p a new DBusServer, or #NULL on failure.
+ * @param error location to store rationale for failure on bad address
+ * @returns the outcome
+ *
+ */
+DBusServerListenResult
+_dbus_server_listen_socket (DBusAddressEntry *entry,
+ DBusServer **server_p,
+ DBusError *error)
+{
+ const char *method;
+
+ *server_p = NULL;
+
+ method = dbus_address_entry_get_method (entry);
+
+ if (strcmp (method, "tcp") == 0 || strcmp (method, "nonce-tcp") == 0)
+ {
+ const char *host;
+ const char *port;
+ const char *bind;
+ const char *family;
+
+ host = dbus_address_entry_get_value (entry, "host");
+ bind = dbus_address_entry_get_value (entry, "bind");
+ port = dbus_address_entry_get_value (entry, "port");
+ family = dbus_address_entry_get_value (entry, "family");
+
+ *server_p = _dbus_server_new_for_tcp_socket (host, bind, port,
+ family, error, strcmp (method, "nonce-tcp") == 0 ? TRUE : FALSE);
+
+ if (*server_p)
+ {
+ _DBUS_ASSERT_ERROR_IS_CLEAR(error);
+ return DBUS_SERVER_LISTEN_OK;
+ }
+ else
+ {
+ _DBUS_ASSERT_ERROR_IS_SET(error);
+ return DBUS_SERVER_LISTEN_DID_NOT_CONNECT;
+ }
+ }
+ else
+ {
+ _DBUS_ASSERT_ERROR_IS_CLEAR(error);
+ return DBUS_SERVER_LISTEN_NOT_HANDLED;
+ }
+}
+
+/**
+ * This is a bad hack since it's really unix domain socket
+ * specific. Also, the function weirdly adopts ownership
+ * of the passed-in string.
+ *
+ * @param server a socket server
+ * @param filename socket filename to report/delete
+ *
+ */
+void
+_dbus_server_socket_own_filename (DBusServer *server,
+ char *filename)
+{
+ DBusServerSocket *socket_server = (DBusServerSocket*) server;
+
+ socket_server->socket_name = filename;
+}
+
+/**
+ * Creates a new server listening on the given Unix domain socket.
+ *
+ * @param path the path for the domain socket.
+ * @param abstract #TRUE to use abstract socket namespace
+ * @param error location to store reason for failure.
+ * @returns the new server, or #NULL on failure.
+ */
+DBusServer*
+_dbus_server_new_for_domain_socket (const char *path,
+ dbus_bool_t abstract,
+ DBusError *error)
+{
+ DBusServer *server;
+ DBusSocket listen_fd;
+ DBusString address;
+ char *path_copy;
+ DBusString path_str;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ if (!_dbus_string_init (&address))
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ return NULL;
+ }
+
+ _dbus_string_init_const (&path_str, path);
+ if ((abstract &&
+ !_dbus_string_append (&address, "unix:abstract=")) ||
+ (!abstract &&
+ !_dbus_string_append (&address, "unix:path=")) ||
+ !_dbus_address_append_escaped (&address, &path_str))
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ goto failed_0;
+ }
+
+ if (abstract)
+ {
+ path_copy = NULL;
+ }
+ else
+ {
+ path_copy = _dbus_strdup (path);
+ if (path_copy == NULL)
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ goto failed_0;
+ }
+ }
+
+ listen_fd = _dbus_listen_unix_socket (path, abstract, error);
+
+ if (!_dbus_socket_is_valid (listen_fd))
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ goto failed_1;
+ }
+
+ server = _dbus_server_new_for_socket (&listen_fd, 1, &address, 0, error);
+ if (server == NULL)
+ {
+ goto failed_2;
+ }
+
+ if (path_copy != NULL)
+ _dbus_server_socket_own_filename(server, path_copy);
+
+ _dbus_string_free (&address);
+
+ return server;
+
+ failed_2:
+ _dbus_close_socket (&listen_fd, NULL);
+ failed_1:
+ dbus_free (path_copy);
+ failed_0:
+ _dbus_string_free (&address);
+
+ return NULL;
+}
+
+/**
+ * Creates a new Unix domain socket server listening under the given directory.
+ * This function is used for "unix:dir/tmpdir" kind of addresses.
+ *
+ * @param dir the path to a directory.
+ * @param error location to store reason for failure.
+ * @returns the new server, or #NULL on failure.
+ */
+static DBusServer *
+_dbus_server_new_for_dir (const char *dir,
+ DBusError *error)
+{
+ DBusServer *server;
+ DBusString full_path;
+ DBusString filename;
+
+ if (!_dbus_string_init (&full_path))
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ return NULL;
+ }
+
+ if (!_dbus_string_init (&filename))
+ {
+ _dbus_string_free (&full_path);
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ return NULL;
+ }
+
+ if (!_dbus_string_append (&filename, "dbus-"))
+ {
+ _dbus_string_free (&full_path);
+ _dbus_string_free (&filename);
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ return NULL;
+ }
+
+ if (!_dbus_generate_random_ascii (&filename, 10, error))
+ {
+ _dbus_string_free (&full_path);
+ _dbus_string_free (&filename);
+ return NULL;
+ }
+
+ if (!_dbus_string_append (&full_path, dir) ||
+ !_dbus_concat_dir_and_file (&full_path, &filename))
+ {
+ _dbus_string_free (&full_path);
+ _dbus_string_free (&filename);
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ return NULL;
+ }
+
+ server =
+ _dbus_server_new_for_domain_socket (_dbus_string_get_const_data (&full_path),
+ FALSE, /* not abstract */
+ error);
+
+ _dbus_string_free (&full_path);
+ _dbus_string_free (&filename);
+
+ return server;
+}
+
+/**
+ * Tries to interpret the address entry for UNIX socket
+ * addresses.
+ *
+ * Sets error if the result is not OK.
+ *
+ * @param entry an address entry
+ * @param server_p location to store a new DBusServer, or #NULL on failure.
+ * @param error location to store rationale for failure on bad address
+ * @returns the outcome
+ *
+ */
+DBusServerListenResult
+_dbus_server_listen_unix_socket (DBusAddressEntry *entry,
+ DBusServer **server_p,
+ DBusError *error)
+{
+ const char *method;
+
+ *server_p = NULL;
+
+ method = dbus_address_entry_get_method (entry);
+
+ if (strcmp (method, "unix") == 0)
+ {
+ const char *path = dbus_address_entry_get_value (entry, "path");
+ const char *dir = dbus_address_entry_get_value (entry, "dir");
+ const char *tmpdir = dbus_address_entry_get_value (entry, "tmpdir");
+ const char *abstract = dbus_address_entry_get_value (entry, "abstract");
+ const char *runtime = dbus_address_entry_get_value (entry, "runtime");
+ int mutually_exclusive_modes = 0;
+
+ mutually_exclusive_modes = (path != NULL) + (tmpdir != NULL) +
+ (abstract != NULL) + (runtime != NULL) + (dir != NULL);
+
+ if (mutually_exclusive_modes < 1)
+ {
+ _dbus_set_bad_address(error, "unix",
+ "path or tmpdir or abstract or runtime or dir",
+ NULL);
+ return DBUS_SERVER_LISTEN_BAD_ADDRESS;
+ }
+
+ if (mutually_exclusive_modes > 1)
+ {
+ _dbus_set_bad_address(error, NULL, NULL,
+ "cannot specify two of \"path\", \"tmpdir\", \"abstract\", \"runtime\" and \"dir\" at the same time");
+ return DBUS_SERVER_LISTEN_BAD_ADDRESS;
+ }
+
+ if (runtime != NULL)
+ {
+ DBusString full_path;
+ DBusString filename;
+ const char *runtimedir;
+
+ if (strcmp (runtime, "yes") != 0)
+ {
+ _dbus_set_bad_address(error, NULL, NULL,
+ "if given, the only value allowed for \"runtime\" is \"yes\"");
+ return DBUS_SERVER_LISTEN_BAD_ADDRESS;
+ }
+
+ runtimedir = _dbus_getenv ("XDG_RUNTIME_DIR");
+
+ if (runtimedir == NULL)
+ {
+ dbus_set_error (error,
+ DBUS_ERROR_NOT_SUPPORTED, "\"XDG_RUNTIME_DIR\" is not set");
+ return DBUS_SERVER_LISTEN_DID_NOT_CONNECT;
+ }
+
+ _dbus_string_init_const (&filename, "bus");
+
+ if (!_dbus_string_init (&full_path))
+ {
+ _DBUS_SET_OOM (error);
+ return DBUS_SERVER_LISTEN_DID_NOT_CONNECT;
+ }
+
+ if (!_dbus_string_append (&full_path, runtimedir) ||
+ !_dbus_concat_dir_and_file (&full_path, &filename))
+ {
+ _dbus_string_free (&full_path);
+ _DBUS_SET_OOM (error);
+ return DBUS_SERVER_LISTEN_DID_NOT_CONNECT;
+ }
+
+ /* We can safely use filesystem sockets in the runtime directory,
+ * and they are preferred because they can be bind-mounted between
+ * Linux containers. */
+ *server_p = _dbus_server_new_for_domain_socket (
+ _dbus_string_get_const_data (&full_path),
+ FALSE, error);
+
+ _dbus_string_free (&full_path);
+ }
+ else if (tmpdir != NULL || dir != NULL)
+ {
+ /* tmpdir is now equivalent to dir. Previously it would try to
+ * use an abstract socket. */
+ if (tmpdir != NULL)
+ dir = tmpdir;
+
+ *server_p = _dbus_server_new_for_dir (dir, error);
+ }
+ else
+ {
+ if (path)
+ *server_p = _dbus_server_new_for_domain_socket (path, FALSE, error);
+ else
+ *server_p = _dbus_server_new_for_domain_socket (abstract, TRUE, error);
+ }
+
+ if (*server_p != NULL)
+ {
+ _DBUS_ASSERT_ERROR_IS_CLEAR(error);
+ return DBUS_SERVER_LISTEN_OK;
+ }
+ else
+ {
+ _DBUS_ASSERT_ERROR_IS_SET(error);
+ return DBUS_SERVER_LISTEN_DID_NOT_CONNECT;
+ }
+ }
+ else
+ {
+ /* If we don't handle the method, we return NULL with the
+ * error unset
+ */
+ _DBUS_ASSERT_ERROR_IS_CLEAR(error);
+ return DBUS_SERVER_LISTEN_NOT_HANDLED;
+ }
+}
+
+
+/** @} */
diff --git a/src/3rdparty/libdbus/dbus/dbus-server-socket.h b/src/3rdparty/libdbus/dbus/dbus-server-socket.h
new file mode 100644
index 00000000..31b086e8
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-server-socket.h
@@ -0,0 +1,61 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-server-socket.h Server implementation for sockets
+ *
+ * Copyright (C) 2002, 2006 Red Hat Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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
+ *
+ */
+#ifndef DBUS_SERVER_SOCKET_H
+#define DBUS_SERVER_SOCKET_H
+
+#include <dbus/dbus-internals.h>
+#include <dbus/dbus-server-protected.h>
+#include <dbus/dbus-nonce.h>
+
+DBUS_BEGIN_DECLS
+
+DBusServer* _dbus_server_new_for_socket (DBusSocket *fds,
+ int n_fds,
+ const DBusString *address,
+ DBusNonceFile *noncefile,
+ DBusError *error);
+DBusServer* _dbus_server_new_for_autolaunch (const DBusString *address,
+ DBusError *error);
+DBUS_PRIVATE_EXPORT
+DBusServer* _dbus_server_new_for_tcp_socket (const char *host,
+ const char *bind,
+ const char *port,
+ const char *family,
+ DBusError *error,
+ dbus_bool_t use_nonce);
+DBusServerListenResult _dbus_server_listen_socket (DBusAddressEntry *entry,
+ DBusServer **server_p,
+ DBusError *error);
+
+
+void _dbus_server_socket_own_filename (DBusServer *server,
+ char *filename);
+
+DBusServer* _dbus_server_new_for_domain_socket (const char *path,
+ dbus_bool_t abstract,
+ DBusError *error);
+DBUS_END_DECLS
+
+#endif /* DBUS_SERVER_SOCKET_H */
diff --git a/src/3rdparty/libdbus/dbus/dbus-server-unix.c b/src/3rdparty/libdbus/dbus/dbus-server-unix.c
new file mode 100644
index 00000000..dd1cb525
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-server-unix.c
@@ -0,0 +1,145 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-server-unix.c Server implementation for Unix network protocols.
+ *
+ * Copyright (C) 2002, 2003, 2004 Red Hat Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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-server-socket.h"
+#include "dbus-server-launchd.h"
+#include "dbus-transport-unix.h"
+#include "dbus-connection-internal.h"
+#include "dbus-sysdeps-unix.h"
+#include "dbus-string.h"
+
+/**
+ * @defgroup DBusServerUnix DBusServer implementations for UNIX
+ * @ingroup DBusInternals
+ * @brief Implementation details of DBusServer on UNIX
+ *
+ * @{
+ */
+
+/**
+ * Tries to interpret the address entry in a platform-specific
+ * way, creating a platform-specific server type if appropriate.
+ * Sets error if the result is not OK.
+ *
+ * @param entry an address entry
+ * @param server_p location to store a new DBusServer, or #NULL on failure.
+ * @param error location to store rationale for failure on bad address
+ * @returns the outcome
+ *
+ */
+DBusServerListenResult
+_dbus_server_listen_platform_specific (DBusAddressEntry *entry,
+ DBusServer **server_p,
+ DBusError *error)
+{
+ const char *method;
+
+ *server_p = NULL;
+
+ method = dbus_address_entry_get_method (entry);
+ if (strcmp (method, "systemd") == 0)
+ {
+ int i, n;
+ DBusSocket *fds;
+ DBusString address;
+
+ n = _dbus_listen_systemd_sockets (&fds, error);
+ if (n < 0)
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ return DBUS_SERVER_LISTEN_DID_NOT_CONNECT;
+ }
+
+ if (!_dbus_string_init (&address))
+ goto systemd_oom;
+
+ for (i = 0; i < n; i++)
+ {
+ if (i > 0)
+ {
+ if (!_dbus_string_append (&address, ";"))
+ goto systemd_oom;
+ }
+ if (!_dbus_append_address_from_socket (fds[i], &address, error))
+ goto systemd_err;
+ }
+
+ *server_p = _dbus_server_new_for_socket (fds, n, &address, NULL, error);
+ if (*server_p == NULL)
+ goto systemd_err;
+
+ dbus_free (fds);
+ _dbus_string_free (&address);
+
+ return DBUS_SERVER_LISTEN_OK;
+
+ systemd_oom:
+ _DBUS_SET_OOM (error);
+ systemd_err:
+ for (i = 0; i < n; i++)
+ {
+ _dbus_close_socket (&fds[i], NULL);
+ }
+ dbus_free (fds);
+ _dbus_string_free (&address);
+
+ return DBUS_SERVER_LISTEN_DID_NOT_CONNECT;
+ }
+#ifdef DBUS_ENABLE_LAUNCHD
+ else if (strcmp (method, "launchd") == 0)
+ {
+ const char *launchd_env_var = dbus_address_entry_get_value (entry, "env");
+ if (launchd_env_var == NULL)
+ {
+ _dbus_set_bad_address (error, "launchd", "env", NULL);
+ return DBUS_SERVER_LISTEN_DID_NOT_CONNECT;
+ }
+ *server_p = _dbus_server_new_for_launchd (launchd_env_var, error);
+
+ if (*server_p != NULL)
+ {
+ _DBUS_ASSERT_ERROR_IS_CLEAR(error);
+ return DBUS_SERVER_LISTEN_OK;
+ }
+ else
+ {
+ _DBUS_ASSERT_ERROR_IS_SET(error);
+ return DBUS_SERVER_LISTEN_DID_NOT_CONNECT;
+ }
+ }
+#endif
+ else
+ {
+ /* If we don't handle the method, we return NULL with the
+ * error unset
+ */
+ _DBUS_ASSERT_ERROR_IS_CLEAR(error);
+ return DBUS_SERVER_LISTEN_NOT_HANDLED;
+ }
+}
+
+/** @} */
diff --git a/src/3rdparty/libdbus/dbus/dbus-server-win.c b/src/3rdparty/libdbus/dbus/dbus-server-win.c
new file mode 100644
index 00000000..af0b697e
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-server-win.c
@@ -0,0 +1,97 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-server-win.c Server implementation for WIN network protocols.
+ *
+ * Copyright (C) 2002, 2003, 2004 Red Hat Inc.
+ * Copyright (C) 2007 Ralf Habacker <ralf.habacker@freenet.de>
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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-server-win.h"
+#include "dbus-server-socket.h"
+
+/**
+ * @defgroup DBusServerWin DBusServer implementations for Windows
+ * @ingroup DBusInternals
+ * @brief Implementation details of DBusServer on Windows
+ *
+ * @{
+ */
+
+/**
+ * Tries to interpret the address entry in a platform-specific
+ * way, creating a platform-specific server type if appropriate.
+ * Sets error if the result is not OK.
+ *
+ * @param entry an address entry
+ * @param server_p location to store a new DBusServer, or #NULL on failure.
+ * @param error location to store rationale for failure on bad address
+ * @returns the outcome
+ *
+ */
+DBusServerListenResult
+_dbus_server_listen_platform_specific (DBusAddressEntry *entry,
+ DBusServer **server_p,
+ DBusError *error)
+{
+ const char *method;
+
+ *server_p = NULL;
+
+ method = dbus_address_entry_get_method (entry);
+
+ if (strcmp (method, "autolaunch") == 0)
+ {
+ const char *host = "localhost";
+ const char *bind = "localhost";
+ const char *port = "0";
+ const char *family = "ipv4";
+ const char *scope = dbus_address_entry_get_value (entry, "scope");
+
+ if (_dbus_daemon_is_session_bus_address_published (scope))
+ return DBUS_SERVER_LISTEN_ADDRESS_ALREADY_USED;
+
+ *server_p = _dbus_server_new_for_tcp_socket (host, bind, port,
+ family, error, FALSE);
+ if (*server_p)
+ {
+ _DBUS_ASSERT_ERROR_IS_CLEAR(error);
+ (*server_p)->published_address =
+ _dbus_daemon_publish_session_bus_address ((*server_p)->address, scope);
+ return DBUS_SERVER_LISTEN_OK;
+ }
+ else
+ {
+ // make sure no handle is open
+ _dbus_daemon_unpublish_session_bus_address ();
+ _DBUS_ASSERT_ERROR_IS_SET(error);
+ return DBUS_SERVER_LISTEN_DID_NOT_CONNECT;
+ }
+ }
+ else
+ {
+ _DBUS_ASSERT_ERROR_IS_CLEAR(error);
+ return DBUS_SERVER_LISTEN_NOT_HANDLED;
+ }
+}
+
+/** @} */
diff --git a/src/3rdparty/libdbus/dbus/dbus-server-win.h b/src/3rdparty/libdbus/dbus/dbus-server-win.h
new file mode 100644
index 00000000..e690404d
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-server-win.h
@@ -0,0 +1,38 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-server-win.h Server implementation for windows network protocols.
+ *
+ * Copyright (C) 2002 Red Hat Inc.
+ * Copyright (C) 2007 Ralf Habacker <ralf.habacker@freenet.de>
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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
+ *
+ */
+#ifndef DBUS_SERVER_WIN_H
+#define DBUS_SERVER_WIN_H
+
+#include <dbus/dbus-internals.h>
+#include <dbus/dbus-server-protected.h>
+
+DBUS_BEGIN_DECLS
+
+/* add definitions here */
+
+DBUS_END_DECLS
+
+#endif /* DBUS_SERVER_WIN_H */
diff --git a/src/3rdparty/libdbus/dbus/dbus-server.c b/src/3rdparty/libdbus/dbus/dbus-server.c
new file mode 100644
index 00000000..27c31477
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-server.c
@@ -0,0 +1,1197 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-server.c DBusServer object
+ *
+ * Copyright (C) 2002, 2003, 2004, 2005 Red Hat Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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-server.h"
+#include "dbus-server-socket.h"
+#include "dbus-string.h"
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
+#include "dbus-server-debug-pipe.h"
+#endif
+#include "dbus-address.h"
+#include "dbus-protocol.h"
+
+/**
+ * @defgroup DBusServer DBusServer
+ * @ingroup DBus
+ * @brief Server that listens for new connections.
+ *
+ * A DBusServer represents a server that other applications
+ * can connect to. Each connection from another application
+ * is represented by a #DBusConnection.
+ *
+ * @todo Thread safety hasn't been tested much for #DBusServer
+ * @todo Need notification to apps of disconnection, may matter for some transports
+ */
+
+/**
+ * @defgroup DBusServerInternals DBusServer implementation details
+ * @ingroup DBusInternals
+ * @brief Implementation details of DBusServer
+ *
+ * @{
+ */
+
+#ifndef _dbus_server_trace_ref
+void
+_dbus_server_trace_ref (DBusServer *server,
+ int old_refcount,
+ int new_refcount,
+ const char *why)
+{
+ static int enabled = -1;
+
+ _dbus_trace_ref ("DBusServer", server, old_refcount, new_refcount, why,
+ "DBUS_SERVER_TRACE", &enabled);
+}
+#endif
+
+/* this is a little fragile since it assumes the address doesn't
+ * already have a guid, but it shouldn't
+ */
+static char*
+copy_address_with_guid_appended (const DBusString *address,
+ const DBusString *guid_hex)
+{
+ DBusString with_guid;
+ char *retval;
+
+ if (!_dbus_string_init (&with_guid))
+ return NULL;
+
+ if (!_dbus_string_copy (address, 0, &with_guid,
+ _dbus_string_get_length (&with_guid)) ||
+ !_dbus_string_append (&with_guid, ",guid=") ||
+ !_dbus_string_copy (guid_hex, 0,
+ &with_guid, _dbus_string_get_length (&with_guid)))
+ {
+ _dbus_string_free (&with_guid);
+ return NULL;
+ }
+
+ retval = NULL;
+ _dbus_string_steal_data (&with_guid, &retval);
+
+ _dbus_string_free (&with_guid);
+
+ return retval; /* may be NULL if steal_data failed */
+}
+
+/**
+ * Initializes the members of the DBusServer base class.
+ * Chained up to by subclass constructors.
+ *
+ * @param server the server.
+ * @param vtable the vtable for the subclass.
+ * @param address the server's address
+ * @param error location to store reason for failure
+ * @returns #TRUE on success.
+ */
+dbus_bool_t
+_dbus_server_init_base (DBusServer *server,
+ const DBusServerVTable *vtable,
+ const DBusString *address,
+ DBusError *error)
+{
+ server->vtable = vtable;
+
+#ifdef DBUS_DISABLE_ASSERT
+ _dbus_atomic_inc (&server->refcount);
+#else
+ {
+ dbus_int32_t old_refcount = _dbus_atomic_inc (&server->refcount);
+
+ _dbus_assert (old_refcount == 0);
+ }
+#endif
+
+ server->address = NULL;
+ server->watches = NULL;
+ server->timeouts = NULL;
+ server->published_address = FALSE;
+
+ if (!_dbus_string_init (&server->guid_hex))
+ {
+ _DBUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ if (!_dbus_generate_uuid (&server->guid, error))
+ goto failed;
+
+ if (!_dbus_uuid_encode (&server->guid, &server->guid_hex))
+ goto oom;
+
+ server->address = copy_address_with_guid_appended (address,
+ &server->guid_hex);
+ if (server->address == NULL)
+ goto oom;
+
+ _dbus_rmutex_new_at_location (&server->mutex);
+ if (server->mutex == NULL)
+ goto oom;
+
+ server->watches = _dbus_watch_list_new ();
+ if (server->watches == NULL)
+ goto oom;
+
+ server->timeouts = _dbus_timeout_list_new ();
+ if (server->timeouts == NULL)
+ goto oom;
+
+ _dbus_data_slot_list_init (&server->slot_list);
+
+ _dbus_verbose ("Initialized server on address %s\n", server->address);
+
+ return TRUE;
+
+ oom:
+ _DBUS_SET_OOM (error);
+ failed:
+ _dbus_rmutex_free_at_location (&server->mutex);
+ server->mutex = NULL;
+ if (server->watches)
+ {
+ _dbus_watch_list_free (server->watches);
+ server->watches = NULL;
+ }
+ if (server->timeouts)
+ {
+ _dbus_timeout_list_free (server->timeouts);
+ server->timeouts = NULL;
+ }
+ if (server->address)
+ {
+ dbus_free (server->address);
+ server->address = NULL;
+ }
+ _dbus_string_free (&server->guid_hex);
+
+ return FALSE;
+}
+
+/**
+ * Finalizes the members of the DBusServer base class.
+ * Chained up to by subclass finalizers.
+ *
+ * @param server the server.
+ */
+void
+_dbus_server_finalize_base (DBusServer *server)
+{
+ /* We don't have the lock, but nobody should be accessing
+ * concurrently since they don't have a ref
+ */
+#ifndef DBUS_DISABLE_CHECKS
+ _dbus_assert (!server->have_server_lock);
+#endif
+ _dbus_assert (server->disconnected);
+
+ /* calls out to application code... */
+ _dbus_data_slot_list_free (&server->slot_list);
+
+ dbus_server_set_new_connection_function (server, NULL, NULL, NULL);
+
+ _dbus_watch_list_free (server->watches);
+ _dbus_timeout_list_free (server->timeouts);
+
+ _dbus_rmutex_free_at_location (&server->mutex);
+
+ dbus_free (server->address);
+
+ dbus_free_string_array (server->auth_mechanisms);
+
+ _dbus_string_free (&server->guid_hex);
+}
+
+
+/** Function to be called in protected_change_watch() with refcount held */
+typedef dbus_bool_t (* DBusWatchAddFunction) (DBusWatchList *list,
+ DBusWatch *watch);
+/** Function to be called in protected_change_watch() with refcount held */
+typedef void (* DBusWatchRemoveFunction) (DBusWatchList *list,
+ DBusWatch *watch);
+/** Function to be called in protected_change_watch() with refcount held */
+typedef void (* DBusWatchToggleFunction) (DBusWatchList *list,
+ DBusWatch *watch,
+ dbus_bool_t enabled);
+
+static dbus_bool_t
+protected_change_watch (DBusServer *server,
+ DBusWatch *watch,
+ DBusWatchAddFunction add_function,
+ DBusWatchRemoveFunction remove_function,
+ DBusWatchToggleFunction toggle_function,
+ dbus_bool_t enabled)
+{
+ DBusWatchList *watches;
+ dbus_bool_t retval;
+
+ HAVE_LOCK_CHECK (server);
+
+ /* This isn't really safe or reasonable; a better pattern is the "do
+ * everything, then drop lock and call out" one; but it has to be
+ * propagated up through all callers
+ */
+
+ watches = server->watches;
+ if (watches)
+ {
+ server->watches = NULL;
+ _dbus_server_ref_unlocked (server);
+ SERVER_UNLOCK (server);
+
+ if (add_function)
+ retval = (* add_function) (watches, watch);
+ else if (remove_function)
+ {
+ retval = TRUE;
+ (* remove_function) (watches, watch);
+ }
+ else
+ {
+ retval = TRUE;
+ (* toggle_function) (watches, watch, enabled);
+ }
+
+ SERVER_LOCK (server);
+ server->watches = watches;
+ _dbus_server_unref_unlocked (server);
+
+ return retval;
+ }
+ else
+ return FALSE;
+}
+
+/**
+ * Adds a watch for this server, chaining out to application-provided
+ * watch handlers.
+ *
+ * @param server the server.
+ * @param watch the watch to add.
+ */
+dbus_bool_t
+_dbus_server_add_watch (DBusServer *server,
+ DBusWatch *watch)
+{
+ HAVE_LOCK_CHECK (server);
+ return protected_change_watch (server, watch,
+ _dbus_watch_list_add_watch,
+ NULL, NULL, FALSE);
+}
+
+/**
+ * Removes a watch previously added with _dbus_server_remove_watch().
+ *
+ * @param server the server.
+ * @param watch the watch to remove.
+ */
+void
+_dbus_server_remove_watch (DBusServer *server,
+ DBusWatch *watch)
+{
+ HAVE_LOCK_CHECK (server);
+ protected_change_watch (server, watch,
+ NULL,
+ _dbus_watch_list_remove_watch,
+ NULL, FALSE);
+}
+
+/**
+ * Toggles all watch and notifies app via server's
+ * DBusWatchToggledFunction if available.
+ *
+ * @param server the server.
+ * @param enabled whether to enable or disable
+ */
+void
+_dbus_server_toggle_all_watches (DBusServer *server,
+ dbus_bool_t enabled)
+{
+ _dbus_watch_list_toggle_all_watches (server->watches, enabled);
+}
+
+/** Function to be called in protected_change_timeout() with refcount held */
+typedef dbus_bool_t (* DBusTimeoutAddFunction) (DBusTimeoutList *list,
+ DBusTimeout *timeout);
+/** Function to be called in protected_change_timeout() with refcount held */
+typedef void (* DBusTimeoutRemoveFunction) (DBusTimeoutList *list,
+ DBusTimeout *timeout);
+/** Function to be called in protected_change_timeout() with refcount held */
+typedef void (* DBusTimeoutToggleFunction) (DBusTimeoutList *list,
+ DBusTimeout *timeout,
+ dbus_bool_t enabled);
+
+
+static dbus_bool_t
+protected_change_timeout (DBusServer *server,
+ DBusTimeout *timeout,
+ DBusTimeoutAddFunction add_function,
+ DBusTimeoutRemoveFunction remove_function,
+ DBusTimeoutToggleFunction toggle_function,
+ dbus_bool_t enabled)
+{
+ DBusTimeoutList *timeouts;
+ dbus_bool_t retval;
+
+ HAVE_LOCK_CHECK (server);
+
+ /* This isn't really safe or reasonable; a better pattern is the "do everything, then
+ * drop lock and call out" one; but it has to be propagated up through all callers
+ */
+
+ timeouts = server->timeouts;
+ if (timeouts)
+ {
+ server->timeouts = NULL;
+ _dbus_server_ref_unlocked (server);
+ SERVER_UNLOCK (server);
+
+ if (add_function)
+ retval = (* add_function) (timeouts, timeout);
+ else if (remove_function)
+ {
+ retval = TRUE;
+ (* remove_function) (timeouts, timeout);
+ }
+ else
+ {
+ retval = TRUE;
+ (* toggle_function) (timeouts, timeout, enabled);
+ }
+
+ SERVER_LOCK (server);
+ server->timeouts = timeouts;
+ _dbus_server_unref_unlocked (server);
+
+ return retval;
+ }
+ else
+ return FALSE;
+}
+
+/**
+ * Adds a timeout for this server, chaining out to
+ * application-provided timeout handlers. The timeout should be
+ * repeatedly handled with dbus_timeout_handle() at its given interval
+ * until it is removed.
+ *
+ * @param server the server.
+ * @param timeout the timeout to add.
+ */
+dbus_bool_t
+_dbus_server_add_timeout (DBusServer *server,
+ DBusTimeout *timeout)
+{
+ return protected_change_timeout (server, timeout,
+ _dbus_timeout_list_add_timeout,
+ NULL, NULL, FALSE);
+}
+
+/**
+ * Removes a timeout previously added with _dbus_server_add_timeout().
+ *
+ * @param server the server.
+ * @param timeout the timeout to remove.
+ */
+void
+_dbus_server_remove_timeout (DBusServer *server,
+ DBusTimeout *timeout)
+{
+ protected_change_timeout (server, timeout,
+ NULL,
+ _dbus_timeout_list_remove_timeout,
+ NULL, FALSE);
+}
+
+/**
+ * Toggles a timeout and notifies app via server's
+ * DBusTimeoutToggledFunction if available. It's an error to call this
+ * function on a timeout that was not previously added.
+ *
+ * @param server the server.
+ * @param timeout the timeout to toggle.
+ * @param enabled whether to enable or disable
+ */
+void
+_dbus_server_toggle_timeout (DBusServer *server,
+ DBusTimeout *timeout,
+ dbus_bool_t enabled)
+{
+ protected_change_timeout (server, timeout,
+ NULL, NULL,
+ _dbus_timeout_list_toggle_timeout,
+ enabled);
+}
+
+
+/**
+ * Like dbus_server_ref() but does not acquire the lock (must already be held)
+ *
+ * @param server the server.
+ */
+void
+_dbus_server_ref_unlocked (DBusServer *server)
+{
+ dbus_int32_t old_refcount;
+
+ _dbus_assert (server != NULL);
+ HAVE_LOCK_CHECK (server);
+
+ old_refcount = _dbus_atomic_inc (&server->refcount);
+ _dbus_assert (old_refcount > 0);
+ _dbus_server_trace_ref (server, old_refcount, old_refcount + 1,
+ "ref_unlocked");
+}
+
+/**
+ * Like dbus_server_unref() but does not acquire the lock (must already be held)
+ *
+ * @param server the server.
+ */
+void
+_dbus_server_unref_unlocked (DBusServer *server)
+{
+ dbus_int32_t old_refcount;
+
+ /* Keep this in sync with dbus_server_unref */
+
+ _dbus_assert (server != NULL);
+
+ HAVE_LOCK_CHECK (server);
+
+ old_refcount = _dbus_atomic_dec (&server->refcount);
+ _dbus_assert (old_refcount > 0);
+
+ _dbus_server_trace_ref (server, old_refcount, old_refcount - 1,
+ "unref_unlocked");
+
+ if (old_refcount == 1)
+ {
+ _dbus_assert (server->disconnected);
+
+ SERVER_UNLOCK (server);
+
+ _dbus_assert (server->vtable->finalize != NULL);
+
+ (* server->vtable->finalize) (server);
+ }
+}
+
+/** @} */
+
+/**
+ * @addtogroup DBusServer
+ *
+ * @{
+ */
+
+
+/**
+ * @typedef DBusServer
+ *
+ * An opaque object representing a server that listens for
+ * connections from other applications. Each time a connection
+ * is made, a new DBusConnection is created and made available
+ * via an application-provided DBusNewConnectionFunction.
+ * The DBusNewConnectionFunction is provided with
+ * dbus_server_set_new_connection_function().
+ *
+ */
+
+static const struct {
+ DBusServerListenResult (* func) (DBusAddressEntry *entry,
+ DBusServer **server_p,
+ DBusError *error);
+} listen_funcs[] = {
+ { _dbus_server_listen_socket }
+ , { _dbus_server_listen_unix_socket }
+ , { _dbus_server_listen_platform_specific }
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
+ , { _dbus_server_listen_debug_pipe }
+#endif
+};
+
+/**
+ * Listens for new connections on the given address. If there are
+ * multiple semicolon-separated address entries in the address, tries
+ * each one and listens on the first one that works.
+ *
+ * Returns #NULL and sets error if listening fails for any reason.
+ * Otherwise returns a new #DBusServer.
+ * dbus_server_set_new_connection_function(),
+ * dbus_server_set_watch_functions(), and
+ * dbus_server_set_timeout_functions() should be called immediately to
+ * render the server fully functional.
+ *
+ * To free the server, applications must call first
+ * dbus_server_disconnect() and then dbus_server_unref().
+ *
+ * @param address the address of this server.
+ * @param error location to store reason for failure.
+ * @returns a new #DBusServer, or #NULL on failure.
+ *
+ */
+DBusServer*
+dbus_server_listen (const char *address,
+ DBusError *error)
+{
+ DBusServer *server;
+ DBusAddressEntry **entries;
+ int len, i;
+ DBusError first_connect_error = DBUS_ERROR_INIT;
+ dbus_bool_t handled_once;
+
+ _dbus_return_val_if_fail (address != NULL, NULL);
+ _dbus_return_val_if_error_is_set (error, NULL);
+
+ if (!dbus_parse_address (address, &entries, &len, error))
+ return NULL;
+
+ server = NULL;
+ handled_once = FALSE;
+
+ for (i = 0; i < len; i++)
+ {
+ int j;
+
+ for (j = 0; j < (int) _DBUS_N_ELEMENTS (listen_funcs); ++j)
+ {
+ DBusServerListenResult result;
+ DBusError tmp_error = DBUS_ERROR_INIT;
+
+ result = (* listen_funcs[j].func) (entries[i],
+ &server,
+ &tmp_error);
+
+ if (result == DBUS_SERVER_LISTEN_OK)
+ {
+ _dbus_assert (server != NULL);
+ _DBUS_ASSERT_ERROR_IS_CLEAR (&tmp_error);
+ handled_once = TRUE;
+ goto out;
+ }
+ else if (result == DBUS_SERVER_LISTEN_ADDRESS_ALREADY_USED)
+ {
+ _dbus_assert (server == NULL);
+ _DBUS_ASSERT_ERROR_IS_CLEAR (&tmp_error);
+ dbus_set_error (error,
+ DBUS_ERROR_ADDRESS_IN_USE,
+ "Address '%s' already used",
+ dbus_address_entry_get_method (entries[0]));
+ handled_once = TRUE;
+ goto out;
+ }
+ else if (result == DBUS_SERVER_LISTEN_BAD_ADDRESS)
+ {
+ _dbus_assert (server == NULL);
+ _DBUS_ASSERT_ERROR_IS_SET (&tmp_error);
+ dbus_move_error (&tmp_error, error);
+ handled_once = TRUE;
+ goto out;
+ }
+ else if (result == DBUS_SERVER_LISTEN_NOT_HANDLED)
+ {
+ _dbus_assert (server == NULL);
+ _DBUS_ASSERT_ERROR_IS_CLEAR (&tmp_error);
+
+ /* keep trying addresses */
+ }
+ else if (result == DBUS_SERVER_LISTEN_DID_NOT_CONNECT)
+ {
+ _dbus_assert (server == NULL);
+ _DBUS_ASSERT_ERROR_IS_SET (&tmp_error);
+ if (!dbus_error_is_set (&first_connect_error))
+ dbus_move_error (&tmp_error, &first_connect_error);
+ else
+ dbus_error_free (&tmp_error);
+
+ handled_once = TRUE;
+
+ /* keep trying addresses */
+ }
+ else
+ {
+ _dbus_assert_not_reached ("Unknown result in dbus_server_listen");
+ }
+ }
+
+ _dbus_assert (server == NULL);
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+ }
+
+ out:
+
+ if (!handled_once)
+ {
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+ if (len > 0)
+ dbus_set_error (error,
+ DBUS_ERROR_BAD_ADDRESS,
+ "Unknown address type '%s'",
+ dbus_address_entry_get_method (entries[0]));
+ else
+ dbus_set_error (error,
+ DBUS_ERROR_BAD_ADDRESS,
+ "Empty address '%s'",
+ address);
+ }
+
+ dbus_address_entries_free (entries);
+
+ if (server == NULL)
+ {
+ _dbus_assert (error == NULL || dbus_error_is_set (&first_connect_error) ||
+ dbus_error_is_set (error));
+
+ if (error && dbus_error_is_set (error))
+ {
+ /* already set the error */
+ }
+ else
+ {
+ /* didn't set the error but either error should be
+ * NULL or first_connect_error should be set.
+ */
+ _dbus_assert (error == NULL || dbus_error_is_set (&first_connect_error));
+ dbus_move_error (&first_connect_error, error);
+ }
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (&first_connect_error); /* be sure we freed it */
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+
+ return NULL;
+ }
+ else
+ {
+ dbus_error_free (&first_connect_error);
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+ return server;
+ }
+}
+
+/**
+ * Increments the reference count of a DBusServer.
+ *
+ * @param server the server.
+ * @returns the server
+ */
+DBusServer *
+dbus_server_ref (DBusServer *server)
+{
+ dbus_int32_t old_refcount;
+
+ _dbus_return_val_if_fail (server != NULL, NULL);
+
+ old_refcount = _dbus_atomic_inc (&server->refcount);
+
+#ifndef DBUS_DISABLE_CHECKS
+ if (_DBUS_UNLIKELY (old_refcount <= 0))
+ {
+ _dbus_atomic_dec (&server->refcount);
+ _dbus_warn_return_if_fail (_DBUS_FUNCTION_NAME, "old_refcount > 0",
+ __FILE__, __LINE__);
+ return NULL;
+ }
+#endif
+
+ _dbus_server_trace_ref (server, old_refcount, old_refcount + 1, "ref");
+
+ return server;
+}
+
+/**
+ * Decrements the reference count of a DBusServer. Finalizes the
+ * server if the reference count reaches zero.
+ *
+ * The server must be disconnected before the refcount reaches zero.
+ *
+ * @param server the server.
+ */
+void
+dbus_server_unref (DBusServer *server)
+{
+ dbus_int32_t old_refcount;
+
+ /* keep this in sync with unref_unlocked */
+
+ _dbus_return_if_fail (server != NULL);
+
+ old_refcount = _dbus_atomic_dec (&server->refcount);
+
+#ifndef DBUS_DISABLE_CHECKS
+ if (_DBUS_UNLIKELY (old_refcount <= 0))
+ {
+ /* undo side-effect first
+ * please do not try to simplify the code here by using
+ * _dbus_atomic_get(), why we don't use it is
+ * because it issues another atomic operation even though
+ * DBUS_DISABLE_CHECKS defined.
+ * Bug: https://bugs.freedesktop.org/show_bug.cgi?id=68303
+ */
+ _dbus_atomic_inc (&server->refcount);
+ _dbus_warn_return_if_fail (_DBUS_FUNCTION_NAME, "old_refcount > 0",
+ __FILE__, __LINE__);
+ return;
+ }
+#endif
+
+ _dbus_server_trace_ref (server, old_refcount, old_refcount - 1, "unref");
+
+ if (old_refcount == 1)
+ {
+ /* lock not held! */
+ _dbus_assert (server->disconnected);
+
+ _dbus_assert (server->vtable->finalize != NULL);
+
+ (* server->vtable->finalize) (server);
+ }
+}
+
+void
+_dbus_server_disconnect_unlocked (DBusServer *server)
+{
+ _dbus_assert (server->vtable->disconnect != NULL);
+
+ if (!server->disconnected)
+ {
+ /* this has to be first so recursive calls to disconnect don't happen */
+ server->disconnected = TRUE;
+
+ (* server->vtable->disconnect) (server);
+ }
+}
+
+/**
+ * Releases the server's address and stops listening for
+ * new clients. If called more than once, only the first
+ * call has an effect. Does not modify the server's
+ * reference count.
+ *
+ * @param server the server.
+ */
+void
+dbus_server_disconnect (DBusServer *server)
+{
+ _dbus_return_if_fail (server != NULL);
+
+ dbus_server_ref (server);
+ SERVER_LOCK (server);
+
+ _dbus_server_disconnect_unlocked (server);
+
+ SERVER_UNLOCK (server);
+ dbus_server_unref (server);
+}
+
+/**
+ * Returns #TRUE if the server is still listening for new connections.
+ *
+ * @param server the server.
+ */
+dbus_bool_t
+dbus_server_get_is_connected (DBusServer *server)
+{
+ dbus_bool_t retval;
+
+ _dbus_return_val_if_fail (server != NULL, FALSE);
+
+ SERVER_LOCK (server);
+ retval = !server->disconnected;
+ SERVER_UNLOCK (server);
+
+ return retval;
+}
+
+/**
+ * Returns the address of the server, as a newly-allocated
+ * string which must be freed by the caller.
+ *
+ * @param server the server
+ * @returns the address or #NULL if no memory
+ */
+char*
+dbus_server_get_address (DBusServer *server)
+{
+ char *retval;
+
+ _dbus_return_val_if_fail (server != NULL, NULL);
+
+ SERVER_LOCK (server);
+ retval = _dbus_strdup (server->address);
+ SERVER_UNLOCK (server);
+
+ return retval;
+}
+
+/**
+ * Returns the unique ID of the server, as a newly-allocated
+ * string which must be freed by the caller. This ID is
+ * normally used by clients to tell when two #DBusConnection
+ * would be equivalent (because the server address passed
+ * to dbus_connection_open() will have the same guid in the
+ * two cases). dbus_connection_open() can re-use an existing
+ * connection with the same ID instead of opening a new
+ * connection.
+ *
+ * This is an ID unique to each #DBusServer. Remember that
+ * a #DBusServer represents only one mode of connecting,
+ * so e.g. a bus daemon can listen on multiple addresses
+ * which will mean it has multiple #DBusServer each with
+ * their own ID.
+ *
+ * The ID is not a UUID in the sense of RFC4122; the details
+ * are explained in the D-Bus specification.
+ *
+ * @param server the server
+ * @returns the id of the server or #NULL if no memory
+ */
+char*
+dbus_server_get_id (DBusServer *server)
+{
+ char *retval;
+
+ _dbus_return_val_if_fail (server != NULL, NULL);
+
+ SERVER_LOCK (server);
+ retval = NULL;
+ _dbus_string_copy_data (&server->guid_hex, &retval);
+ SERVER_UNLOCK (server);
+
+ return retval;
+}
+
+/**
+ * Sets a function to be used for handling new connections. The given
+ * function is passed each new connection as the connection is
+ * created. If the new connection function increments the connection's
+ * reference count, the connection will stay alive. Otherwise, the
+ * connection will be unreferenced and closed. The new connection
+ * function may also close the connection itself, which is considered
+ * good form if the connection is not wanted.
+ *
+ * The connection here is private in the sense of
+ * dbus_connection_open_private(), so if the new connection function
+ * keeps a reference it must arrange for the connection to be closed.
+ * i.e. libdbus does not own this connection once the new connection
+ * function takes a reference.
+ *
+ * @param server the server.
+ * @param function a function to handle new connections.
+ * @param data data to pass to the new connection handler.
+ * @param free_data_function function to free the data.
+ */
+void
+dbus_server_set_new_connection_function (DBusServer *server,
+ DBusNewConnectionFunction function,
+ void *data,
+ DBusFreeFunction free_data_function)
+{
+ DBusFreeFunction old_free_function;
+ void *old_data;
+
+ _dbus_return_if_fail (server != NULL);
+
+ SERVER_LOCK (server);
+ old_free_function = server->new_connection_free_data_function;
+ old_data = server->new_connection_data;
+
+ server->new_connection_function = function;
+ server->new_connection_data = data;
+ server->new_connection_free_data_function = free_data_function;
+ SERVER_UNLOCK (server);
+
+ if (old_free_function != NULL)
+ (* old_free_function) (old_data);
+}
+
+/**
+ * Sets the watch functions for the server. These functions are
+ * responsible for making the application's main loop aware of file
+ * descriptors that need to be monitored for events.
+ *
+ * This function behaves exactly like dbus_connection_set_watch_functions();
+ * see the documentation for that routine.
+ *
+ * @param server the server.
+ * @param add_function function to begin monitoring a new descriptor.
+ * @param remove_function function to stop monitoring a descriptor.
+ * @param toggled_function function to notify when the watch is enabled/disabled
+ * @param data data to pass to add_function and remove_function.
+ * @param free_data_function function to be called to free the data.
+ * @returns #FALSE on failure (no memory)
+ */
+dbus_bool_t
+dbus_server_set_watch_functions (DBusServer *server,
+ DBusAddWatchFunction add_function,
+ DBusRemoveWatchFunction remove_function,
+ DBusWatchToggledFunction toggled_function,
+ void *data,
+ DBusFreeFunction free_data_function)
+{
+ dbus_bool_t result;
+ DBusWatchList *watches;
+
+ _dbus_return_val_if_fail (server != NULL, FALSE);
+
+ SERVER_LOCK (server);
+ watches = server->watches;
+ server->watches = NULL;
+ if (watches)
+ {
+ SERVER_UNLOCK (server);
+ result = _dbus_watch_list_set_functions (watches,
+ add_function,
+ remove_function,
+ toggled_function,
+ data,
+ free_data_function);
+ SERVER_LOCK (server);
+ }
+ else
+ {
+ _dbus_warn_check_failed ("Re-entrant call to %s", _DBUS_FUNCTION_NAME);
+ result = FALSE;
+ }
+ server->watches = watches;
+ SERVER_UNLOCK (server);
+
+ return result;
+}
+
+/**
+ * Sets the timeout functions for the server. These functions are
+ * responsible for making the application's main loop aware of timeouts.
+ *
+ * This function behaves exactly like dbus_connection_set_timeout_functions();
+ * see the documentation for that routine.
+ *
+ * @param server the server.
+ * @param add_function function to add a timeout.
+ * @param remove_function function to remove a timeout.
+ * @param toggled_function function to notify when the timeout is enabled/disabled
+ * @param data data to pass to add_function and remove_function.
+ * @param free_data_function function to be called to free the data.
+ * @returns #FALSE on failure (no memory)
+ */
+dbus_bool_t
+dbus_server_set_timeout_functions (DBusServer *server,
+ DBusAddTimeoutFunction add_function,
+ DBusRemoveTimeoutFunction remove_function,
+ DBusTimeoutToggledFunction toggled_function,
+ void *data,
+ DBusFreeFunction free_data_function)
+{
+ dbus_bool_t result;
+ DBusTimeoutList *timeouts;
+
+ _dbus_return_val_if_fail (server != NULL, FALSE);
+
+ SERVER_LOCK (server);
+ timeouts = server->timeouts;
+ server->timeouts = NULL;
+ if (timeouts)
+ {
+ SERVER_UNLOCK (server);
+ result = _dbus_timeout_list_set_functions (timeouts,
+ add_function,
+ remove_function,
+ toggled_function,
+ data,
+ free_data_function);
+ SERVER_LOCK (server);
+ }
+ else
+ {
+ _dbus_warn_check_failed ("Re-entrant call to %s", _DBUS_FUNCTION_NAME);
+ result = FALSE;
+ }
+ server->timeouts = timeouts;
+ SERVER_UNLOCK (server);
+
+ return result;
+}
+
+/**
+ * Sets the authentication mechanisms that this server offers to
+ * clients, as a #NULL-terminated array of mechanism names. This
+ * function only affects connections created <em>after</em> it is
+ * called. Pass #NULL instead of an array to use all available
+ * mechanisms (this is the default behavior).
+ *
+ * The D-Bus specification describes some of the supported mechanisms.
+ *
+ * @param server the server
+ * @param mechanisms #NULL-terminated array of mechanisms
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+dbus_server_set_auth_mechanisms (DBusServer *server,
+ const char **mechanisms)
+{
+ char **copy;
+
+ _dbus_return_val_if_fail (server != NULL, FALSE);
+
+ SERVER_LOCK (server);
+
+ if (mechanisms != NULL)
+ {
+ copy = _dbus_dup_string_array (mechanisms);
+ if (copy == NULL)
+ {
+ SERVER_UNLOCK (server);
+ return FALSE;
+ }
+ }
+ else
+ copy = NULL;
+
+ dbus_free_string_array (server->auth_mechanisms);
+ server->auth_mechanisms = copy;
+
+ SERVER_UNLOCK (server);
+
+ return TRUE;
+}
+
+static DBusDataSlotAllocator slot_allocator =
+ _DBUS_DATA_SLOT_ALLOCATOR_INIT (_DBUS_LOCK_NAME (server_slots));
+
+/**
+ * Allocates an integer ID to be used for storing application-specific
+ * data on any DBusServer. The allocated ID may then be used
+ * with dbus_server_set_data() and dbus_server_get_data().
+ * The slot must be initialized with -1. If a nonnegative
+ * slot is passed in, the refcount is incremented on that
+ * slot, rather than creating a new slot.
+ *
+ * The allocated slot is global, i.e. all DBusServer objects will have
+ * a slot with the given integer ID reserved.
+ *
+ * @param slot_p address of global variable storing the slot ID
+ * @returns #FALSE on no memory
+ */
+dbus_bool_t
+dbus_server_allocate_data_slot (dbus_int32_t *slot_p)
+{
+ return _dbus_data_slot_allocator_alloc (&slot_allocator,
+ slot_p);
+}
+
+/**
+ * Deallocates a global ID for server data slots.
+ * dbus_server_get_data() and dbus_server_set_data()
+ * may no longer be used with this slot.
+ * Existing data stored on existing DBusServer objects
+ * will be freed when the server is finalized,
+ * but may not be retrieved (and may only be replaced
+ * if someone else reallocates the slot).
+ *
+ * @param slot_p address of the slot to deallocate
+ */
+void
+dbus_server_free_data_slot (dbus_int32_t *slot_p)
+{
+ _dbus_return_if_fail (*slot_p >= 0);
+
+ _dbus_data_slot_allocator_free (&slot_allocator, slot_p);
+}
+
+/**
+ * Stores a pointer on a DBusServer, along
+ * with an optional function to be used for freeing
+ * the data when the data is set again, or when
+ * the server is finalized. The slot number
+ * must have been allocated with dbus_server_allocate_data_slot().
+ *
+ * @param server the server
+ * @param slot the slot number
+ * @param data the data to store
+ * @param free_data_func finalizer function for the data
+ * @returns #TRUE if there was enough memory to store the data
+ */
+dbus_bool_t
+dbus_server_set_data (DBusServer *server,
+ int slot,
+ void *data,
+ DBusFreeFunction free_data_func)
+{
+ DBusFreeFunction old_free_func;
+ void *old_data;
+ dbus_bool_t retval;
+
+ _dbus_return_val_if_fail (server != NULL, FALSE);
+
+ SERVER_LOCK (server);
+
+ retval = _dbus_data_slot_list_set (&slot_allocator,
+ &server->slot_list,
+ slot, data, free_data_func,
+ &old_free_func, &old_data);
+
+
+ SERVER_UNLOCK (server);
+
+ if (retval)
+ {
+ /* Do the actual free outside the server lock */
+ if (old_free_func)
+ (* old_free_func) (old_data);
+ }
+
+ return retval;
+}
+
+/**
+ * Retrieves data previously set with dbus_server_set_data().
+ * The slot must still be allocated (must not have been freed).
+ *
+ * @param server the server
+ * @param slot the slot to get data from
+ * @returns the data, or #NULL if not found
+ */
+void*
+dbus_server_get_data (DBusServer *server,
+ int slot)
+{
+ void *res;
+
+ _dbus_return_val_if_fail (server != NULL, NULL);
+
+ SERVER_LOCK (server);
+
+ res = _dbus_data_slot_list_get (&slot_allocator,
+ &server->slot_list,
+ slot);
+
+ SERVER_UNLOCK (server);
+
+ return res;
+}
+
+/** @} */
diff --git a/src/3rdparty/libdbus/dbus/dbus-server.h b/src/3rdparty/libdbus/dbus/dbus-server.h
new file mode 100644
index 00000000..1f8c5949
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-server.h
@@ -0,0 +1,127 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-server.h DBusServer object
+ *
+ * Copyright (C) 2002, 2003 Red Hat Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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
+ *
+ */
+#if !defined (DBUS_INSIDE_DBUS_H) && !defined (DBUS_COMPILATION)
+#error "Only <dbus/dbus.h> can be included directly, this file may disappear or change contents."
+#endif
+
+#ifndef DBUS_SERVER_H
+#define DBUS_SERVER_H
+
+#include <dbus/dbus-errors.h>
+#include <dbus/dbus-macros.h>
+#include <dbus/dbus-message.h>
+#include <dbus/dbus-connection.h>
+#include <dbus/dbus-protocol.h>
+
+DBUS_BEGIN_DECLS
+
+/**
+ * @addtogroup DBusServer
+ * @{
+ */
+
+typedef struct DBusServer DBusServer;
+
+/** Called when a new connection to the server is available. Must reference and save the new
+ * connection, or close the new connection. Set with dbus_server_set_new_connection_function().
+ */
+typedef void (* DBusNewConnectionFunction) (DBusServer *server,
+ DBusConnection *new_connection,
+ void *data);
+
+DBUS_EXPORT
+DBusServer* dbus_server_listen (const char *address,
+ DBusError *error);
+DBUS_EXPORT
+DBusServer* dbus_server_ref (DBusServer *server);
+DBUS_EXPORT
+void dbus_server_unref (DBusServer *server);
+DBUS_EXPORT
+void dbus_server_disconnect (DBusServer *server);
+DBUS_EXPORT
+dbus_bool_t dbus_server_get_is_connected (DBusServer *server);
+DBUS_EXPORT
+char* dbus_server_get_address (DBusServer *server);
+DBUS_EXPORT
+char* dbus_server_get_id (DBusServer *server);
+DBUS_EXPORT
+void dbus_server_set_new_connection_function (DBusServer *server,
+ DBusNewConnectionFunction function,
+ void *data,
+ DBusFreeFunction free_data_function);
+DBUS_EXPORT
+dbus_bool_t dbus_server_set_watch_functions (DBusServer *server,
+ DBusAddWatchFunction add_function,
+ DBusRemoveWatchFunction remove_function,
+ DBusWatchToggledFunction toggled_function,
+ void *data,
+ DBusFreeFunction free_data_function);
+DBUS_EXPORT
+dbus_bool_t dbus_server_set_timeout_functions (DBusServer *server,
+ DBusAddTimeoutFunction add_function,
+ DBusRemoveTimeoutFunction remove_function,
+ DBusTimeoutToggledFunction toggled_function,
+ void *data,
+ DBusFreeFunction free_data_function);
+DBUS_EXPORT
+dbus_bool_t dbus_server_set_auth_mechanisms (DBusServer *server,
+ const char **mechanisms);
+
+DBUS_EXPORT
+dbus_bool_t dbus_server_allocate_data_slot (dbus_int32_t *slot_p);
+DBUS_EXPORT
+void dbus_server_free_data_slot (dbus_int32_t *slot_p);
+DBUS_EXPORT
+dbus_bool_t dbus_server_set_data (DBusServer *server,
+ int slot,
+ void *data,
+ DBusFreeFunction free_data_func);
+DBUS_EXPORT
+void* dbus_server_get_data (DBusServer *server,
+ int slot);
+
+/**
+ * Clear a variable or struct member that contains a #DBusServer.
+ * If it does not contain #NULL, the server that was previously
+ * there is unreferenced with dbus_server_unref().
+ *
+ * This is very similar to dbus_clear_connection(): see that function
+ * for more details.
+ *
+ * @param pointer_to_server A pointer to a variable or struct member.
+ * pointer_to_server must not be #NULL, but *pointer_to_server
+ * may be #NULL.
+ */
+static inline void
+dbus_clear_server (DBusServer **pointer_to_server)
+{
+ _dbus_clear_pointer_impl (DBusServer, pointer_to_server, dbus_server_unref);
+}
+
+/** @} */
+
+DBUS_END_DECLS
+
+#endif /* DBUS_SERVER_H */
diff --git a/src/3rdparty/libdbus/dbus/dbus-sha.c b/src/3rdparty/libdbus/dbus/dbus-sha.c
new file mode 100644
index 00000000..effce82c
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-sha.c
@@ -0,0 +1,513 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-sha.c SHA-1 implementation
+ *
+ * Copyright (C) 2003 Red Hat Inc.
+ * Copyright (C) 1995 A. M. Kuchling
+ * SPDX-License-Identifier: (AFL-2.1 OR GPL-2.0-or-later) AND LicenseRef-pycrypto-orig
+ *
+ * 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-sha.h"
+#include "dbus-marshal-basic.h" /* for byteswap routines */
+#include <string.h>
+
+/* The following comments have the history of where this code
+ * comes from. I actually copied it from GNet in GNOME CVS.
+ * - hp@redhat.com
+ */
+
+/*
+ * sha.h : Implementation of the Secure Hash Algorithm
+ *
+ * Part of the Python Cryptography Toolkit, version 1.0.0
+ *
+ * Copyright (C) 1995, A.M. Kuchling
+ *
+ * Distribute and use freely; there are no restrictions on further
+ * dissemination and usage except those imposed by the laws of your
+ * country of residence.
+ *
+ */
+
+/* SHA: NIST's Secure Hash Algorithm */
+
+/* Based on SHA code originally posted to sci.crypt by Peter Gutmann
+ in message <30ajo5$oe8@ccu2.auckland.ac.nz>.
+ Modified to test for endianness on creation of SHA objects by AMK.
+ Also, the original specification of SHA was found to have a weakness
+ by NSA/NIST. This code implements the fixed version of SHA.
+*/
+
+/* Here's the first paragraph of Peter Gutmann's posting:
+
+The following is my SHA (FIPS 180) code updated to allow use of the "fixed"
+SHA, thanks to Jim Gillogly and an anonymous contributor for the information on
+what's changed in the new version. The fix is a simple change which involves
+adding a single rotate in the initial expansion function. It is unknown
+whether this is an optimal solution to the problem which was discovered in the
+SHA or whether it's simply a bandaid which fixes the problem with a minimum of
+effort (for example the reengineering of a great many Capstone chips).
+*/
+
+/**
+ * @defgroup DBusSHA SHA implementation
+ * @ingroup DBusInternals
+ * @brief SHA-1 hash
+ *
+ * Types and functions related to computing SHA-1 hash.
+ */
+
+/**
+ * @defgroup DBusSHAInternals SHA implementation details
+ * @ingroup DBusInternals
+ * @brief Internals of SHA implementation.
+ *
+ * The implementation of SHA-1 (see http://www.itl.nist.gov/fipspubs/fip180-1.htm).
+ * This SHA implementation was written by A.M. Kuchling
+ *
+ * @{
+ */
+
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+
+/* The SHA block size and message digest sizes, in bytes */
+
+#define SHA_DATASIZE 64
+#define SHA_DIGESTSIZE 20
+
+/* The SHA f()-functions. The f1 and f3 functions can be optimized to
+ save one boolean operation each - thanks to Rich Schroeppel,
+ rcs@cs.arizona.edu for discovering this */
+
+/*#define f1(x,y,z) ( ( x & y ) | ( ~x & z ) ) // Rounds 0-19 */
+#define f1(x,y,z) ( z ^ ( x & ( y ^ z ) ) ) /* Rounds 0-19 */
+#define f2(x,y,z) ( x ^ y ^ z ) /* Rounds 20-39 */
+/*#define f3(x,y,z) ( ( x & y ) | ( x & z ) | ( y & z ) ) // Rounds 40-59 */
+#define f3(x,y,z) ( ( x & y ) | ( z & ( x | y ) ) ) /* Rounds 40-59 */
+#define f4(x,y,z) ( x ^ y ^ z ) /* Rounds 60-79 */
+
+/* The SHA Mysterious Constants */
+
+#define K1 0x5A827999L /* Rounds 0-19 */
+#define K2 0x6ED9EBA1L /* Rounds 20-39 */
+#define K3 0x8F1BBCDCL /* Rounds 40-59 */
+#define K4 0xCA62C1D6L /* Rounds 60-79 */
+
+/* SHA initial values */
+
+#define h0init 0x67452301L
+#define h1init 0xEFCDAB89L
+#define h2init 0x98BADCFEL
+#define h3init 0x10325476L
+#define h4init 0xC3D2E1F0L
+
+/* Note that it may be necessary to add parentheses to these macros if they
+ are to be called with expressions as arguments */
+/* 32-bit rotate left - kludged with shifts */
+
+#define ROTL(n,X) ( ( ( X ) << n ) | ( ( X ) >> ( 32 - n ) ) )
+
+/* The initial expanding function. The hash function is defined over an
+ 80-word expanded input array W, where the first 16 are copies of the input
+ data, and the remaining 64 are defined by
+
+ W[ i ] = W[ i - 16 ] ^ W[ i - 14 ] ^ W[ i - 8 ] ^ W[ i - 3 ]
+
+ This implementation generates these values on the fly in a circular
+ buffer - thanks to Colin Plumb, colin@nyx10.cs.du.edu for this
+ optimization.
+
+ The updated SHA changes the expanding function by adding a rotate of 1
+ bit. Thanks to Jim Gillogly, jim@rand.org, and an anonymous contributor
+ for this information */
+
+#define expand(W,i) ( W[ i & 15 ] = ROTL( 1, ( W[ i & 15 ] ^ W[ (i - 14) & 15 ] ^ \
+ W[ (i - 8) & 15 ] ^ W[ (i - 3) & 15 ] ) ) )
+
+
+/* The prototype SHA sub-round. The fundamental sub-round is:
+
+ a' = e + ROTL( 5, a ) + f( b, c, d ) + k + data;
+ b' = a;
+ c' = ROTL( 30, b );
+ d' = c;
+ e' = d;
+
+ but this is implemented by unrolling the loop 5 times and renaming the
+ variables ( e, a, b, c, d ) = ( a', b', c', d', e' ) each iteration.
+ This code is then replicated 20 times for each of the 4 functions, using
+ the next 20 values from the W[] array each time */
+
+#define subRound(a, b, c, d, e, f, k, data) \
+ ( e += ROTL( 5, a ) + f( b, c, d ) + k + data, b = ROTL( 30, b ) )
+
+#endif /* !DOXYGEN_SHOULD_SKIP_THIS */
+
+/* Perform the SHA transformation. Note that this code, like MD5, seems to
+ break some optimizing compilers due to the complexity of the expressions
+ and the size of the basic block. It may be necessary to split it into
+ sections, e.g. based on the four subrounds
+
+ Note that this corrupts the context->data area */
+
+static void
+SHATransform(dbus_uint32_t *digest, dbus_uint32_t *data)
+{
+ dbus_uint32_t A, B, C, D, E; /* Local vars */
+ dbus_uint32_t eData[16]; /* Expanded data */
+
+ /* Set up first buffer and local data buffer */
+ A = digest[0];
+ B = digest[1];
+ C = digest[2];
+ D = digest[3];
+ E = digest[4];
+ memmove (eData, data, SHA_DATASIZE);
+
+ /* Heavy mangling, in 4 sub-rounds of 20 interations each. */
+ subRound (A, B, C, D, E, f1, K1, eData[0]);
+ subRound (E, A, B, C, D, f1, K1, eData[1]);
+ subRound (D, E, A, B, C, f1, K1, eData[2]);
+ subRound (C, D, E, A, B, f1, K1, eData[3]);
+ subRound (B, C, D, E, A, f1, K1, eData[4]);
+ subRound (A, B, C, D, E, f1, K1, eData[5]);
+ subRound (E, A, B, C, D, f1, K1, eData[6]);
+ subRound (D, E, A, B, C, f1, K1, eData[7]);
+ subRound (C, D, E, A, B, f1, K1, eData[8]);
+ subRound (B, C, D, E, A, f1, K1, eData[9]);
+ subRound (A, B, C, D, E, f1, K1, eData[10]);
+ subRound (E, A, B, C, D, f1, K1, eData[11]);
+ subRound (D, E, A, B, C, f1, K1, eData[12]);
+ subRound (C, D, E, A, B, f1, K1, eData[13]);
+ subRound (B, C, D, E, A, f1, K1, eData[14]);
+ subRound (A, B, C, D, E, f1, K1, eData[15]);
+ subRound (E, A, B, C, D, f1, K1, expand ( eData, 16) );
+ subRound (D, E, A, B, C, f1, K1, expand ( eData, 17) );
+ subRound (C, D, E, A, B, f1, K1, expand ( eData, 18) );
+ subRound (B, C, D, E, A, f1, K1, expand ( eData, 19) );
+
+ subRound (A, B, C, D, E, f2, K2, expand ( eData, 20) );
+ subRound (E, A, B, C, D, f2, K2, expand ( eData, 21) );
+ subRound (D, E, A, B, C, f2, K2, expand ( eData, 22) );
+ subRound (C, D, E, A, B, f2, K2, expand ( eData, 23) );
+ subRound (B, C, D, E, A, f2, K2, expand ( eData, 24) );
+ subRound (A, B, C, D, E, f2, K2, expand ( eData, 25) );
+ subRound (E, A, B, C, D, f2, K2, expand ( eData, 26) );
+ subRound (D, E, A, B, C, f2, K2, expand ( eData, 27) );
+ subRound (C, D, E, A, B, f2, K2, expand ( eData, 28) );
+ subRound (B, C, D, E, A, f2, K2, expand ( eData, 29) );
+ subRound (A, B, C, D, E, f2, K2, expand ( eData, 30) );
+ subRound (E, A, B, C, D, f2, K2, expand ( eData, 31) );
+ subRound (D, E, A, B, C, f2, K2, expand ( eData, 32) );
+ subRound (C, D, E, A, B, f2, K2, expand ( eData, 33) );
+ subRound (B, C, D, E, A, f2, K2, expand ( eData, 34) );
+ subRound (A, B, C, D, E, f2, K2, expand ( eData, 35) );
+ subRound (E, A, B, C, D, f2, K2, expand ( eData, 36) );
+ subRound (D, E, A, B, C, f2, K2, expand ( eData, 37) );
+ subRound (C, D, E, A, B, f2, K2, expand ( eData, 38) );
+ subRound (B, C, D, E, A, f2, K2, expand ( eData, 39) );
+
+ subRound (A, B, C, D, E, f3, K3, expand ( eData, 40) );
+ subRound (E, A, B, C, D, f3, K3, expand ( eData, 41) );
+ subRound (D, E, A, B, C, f3, K3, expand ( eData, 42) );
+ subRound (C, D, E, A, B, f3, K3, expand ( eData, 43) );
+ subRound (B, C, D, E, A, f3, K3, expand ( eData, 44) );
+ subRound (A, B, C, D, E, f3, K3, expand ( eData, 45) );
+ subRound (E, A, B, C, D, f3, K3, expand ( eData, 46) );
+ subRound (D, E, A, B, C, f3, K3, expand ( eData, 47) );
+ subRound (C, D, E, A, B, f3, K3, expand ( eData, 48) );
+ subRound (B, C, D, E, A, f3, K3, expand ( eData, 49) );
+ subRound (A, B, C, D, E, f3, K3, expand ( eData, 50) );
+ subRound (E, A, B, C, D, f3, K3, expand ( eData, 51) );
+ subRound (D, E, A, B, C, f3, K3, expand ( eData, 52) );
+ subRound (C, D, E, A, B, f3, K3, expand ( eData, 53) );
+ subRound (B, C, D, E, A, f3, K3, expand ( eData, 54) );
+ subRound (A, B, C, D, E, f3, K3, expand ( eData, 55) );
+ subRound (E, A, B, C, D, f3, K3, expand ( eData, 56) );
+ subRound (D, E, A, B, C, f3, K3, expand ( eData, 57) );
+ subRound (C, D, E, A, B, f3, K3, expand ( eData, 58) );
+ subRound (B, C, D, E, A, f3, K3, expand ( eData, 59) );
+
+ subRound (A, B, C, D, E, f4, K4, expand ( eData, 60) );
+ subRound (E, A, B, C, D, f4, K4, expand ( eData, 61) );
+ subRound (D, E, A, B, C, f4, K4, expand ( eData, 62) );
+ subRound (C, D, E, A, B, f4, K4, expand ( eData, 63) );
+ subRound (B, C, D, E, A, f4, K4, expand ( eData, 64) );
+ subRound (A, B, C, D, E, f4, K4, expand ( eData, 65) );
+ subRound (E, A, B, C, D, f4, K4, expand ( eData, 66) );
+ subRound (D, E, A, B, C, f4, K4, expand ( eData, 67) );
+ subRound (C, D, E, A, B, f4, K4, expand ( eData, 68) );
+ subRound (B, C, D, E, A, f4, K4, expand ( eData, 69) );
+ subRound (A, B, C, D, E, f4, K4, expand ( eData, 70) );
+ subRound (E, A, B, C, D, f4, K4, expand ( eData, 71) );
+ subRound (D, E, A, B, C, f4, K4, expand ( eData, 72) );
+ subRound (C, D, E, A, B, f4, K4, expand ( eData, 73) );
+ subRound (B, C, D, E, A, f4, K4, expand ( eData, 74) );
+ subRound (A, B, C, D, E, f4, K4, expand ( eData, 75) );
+ subRound (E, A, B, C, D, f4, K4, expand ( eData, 76) );
+ subRound (D, E, A, B, C, f4, K4, expand ( eData, 77) );
+ subRound (C, D, E, A, B, f4, K4, expand ( eData, 78) );
+ subRound (B, C, D, E, A, f4, K4, expand ( eData, 79) );
+
+ /* Build message digest */
+ digest[0] += A;
+ digest[1] += B;
+ digest[2] += C;
+ digest[3] += D;
+ digest[4] += E;
+}
+
+/* When run on a little-endian CPU we need to perform byte reversal on an
+ array of longwords. */
+
+#ifdef WORDS_BIGENDIAN
+#define swap_words(buffer, byte_count)
+#else
+static void
+swap_words (dbus_uint32_t *buffer,
+ int byte_count)
+{
+ byte_count /= sizeof (dbus_uint32_t);
+ while (byte_count--)
+ {
+ *buffer = DBUS_UINT32_SWAP_LE_BE (*buffer);
+ ++buffer;
+ }
+}
+#endif
+
+static void
+sha_init (DBusSHAContext *context)
+{
+ /* Set the h-vars to their initial values */
+ context->digest[0] = h0init;
+ context->digest[1] = h1init;
+ context->digest[2] = h2init;
+ context->digest[3] = h3init;
+ context->digest[4] = h4init;
+
+ /* Initialise bit count */
+ context->count_lo = context->count_hi = 0;
+}
+
+static void
+sha_append (DBusSHAContext *context,
+ const unsigned char *buffer,
+ unsigned int count)
+{
+ dbus_uint32_t tmp;
+ unsigned int dataCount;
+
+ /* Update bitcount */
+ tmp = context->count_lo;
+ if (( context->count_lo = tmp + ( ( dbus_uint32_t) count << 3) ) < tmp)
+ context->count_hi++; /* Carry from low to high */
+ context->count_hi += count >> 29;
+
+ /* Get count of bytes already in data */
+ dataCount = (int) (tmp >> 3) & 0x3F;
+
+ /* Handle any leading odd-sized chunks */
+ if (dataCount)
+ {
+ unsigned char *p = (unsigned char *) context->data + dataCount;
+
+ dataCount = SHA_DATASIZE - dataCount;
+ if (count < dataCount)
+ {
+ memmove (p, buffer, count);
+ return;
+ }
+ memmove (p, buffer, dataCount);
+ swap_words (context->data, SHA_DATASIZE);
+ SHATransform (context->digest, context->data);
+ buffer += dataCount;
+ count -= dataCount;
+ }
+
+ /* Process data in SHA_DATASIZE chunks */
+ while (count >= SHA_DATASIZE)
+ {
+ memmove (context->data, buffer, SHA_DATASIZE);
+ swap_words (context->data, SHA_DATASIZE);
+ SHATransform (context->digest, context->data);
+ buffer += SHA_DATASIZE;
+ count -= SHA_DATASIZE;
+ }
+
+ /* Handle any remaining bytes of data. */
+ memmove (context->data, buffer, count);
+}
+
+
+/* Final wrapup - pad to SHA_DATASIZE-byte boundary with the bit pattern
+ 1 0* (64-bit count of bits processed, MSB-first) */
+
+static void
+sha_finish (DBusSHAContext *context, unsigned char digest[20])
+{
+ int count;
+ unsigned char *data_p;
+
+ /* Compute number of bytes mod 64 */
+ count = (int) context->count_lo;
+ count = (count >> 3) & 0x3F;
+
+ /* Set the first char of padding to 0x80. This is safe since there is
+ always at least one byte free */
+ data_p = (unsigned char *) context->data + count;
+ *data_p++ = 0x80;
+
+ /* Bytes of padding needed to make 64 bytes */
+ count = SHA_DATASIZE - 1 - count;
+
+ /* Pad out to 56 mod 64 */
+ if (count < 8)
+ {
+ /* Two lots of padding: Pad the first block to 64 bytes */
+ memset (data_p, 0, count);
+ swap_words (context->data, SHA_DATASIZE);
+ SHATransform (context->digest, context->data);
+
+ /* Now fill the next block with 56 bytes */
+ memset (context->data, 0, SHA_DATASIZE - 8);
+ }
+ else
+ /* Pad block to 56 bytes */
+ memset (data_p, 0, count - 8);
+
+ /* Append length in bits and transform */
+ context->data[14] = context->count_hi;
+ context->data[15] = context->count_lo;
+
+ swap_words (context->data, SHA_DATASIZE - 8);
+ SHATransform (context->digest, context->data);
+ swap_words (context->digest, SHA_DIGESTSIZE);
+ memmove (digest, context->digest, SHA_DIGESTSIZE);
+}
+
+/** @} */ /* End of internals */
+
+/**
+ * @addtogroup DBusSHA
+ *
+ * @{
+ */
+
+/**
+ * Initializes the SHA context.
+ *
+ * @param context an uninitialized context, typically on the stack.
+ */
+void
+_dbus_sha_init (DBusSHAContext *context)
+{
+ sha_init (context);
+}
+
+/**
+ * Feeds more data into an existing shasum computation.
+ *
+ * @param context the SHA context
+ * @param data the additional data to hash
+ */
+void
+_dbus_sha_update (DBusSHAContext *context,
+ const DBusString *data)
+{
+ unsigned int inputLen;
+ const unsigned char *input;
+
+ input = (const unsigned char*) _dbus_string_get_const_data (data);
+ inputLen = _dbus_string_get_length (data);
+
+ sha_append (context, input, inputLen);
+}
+
+/**
+ * SHA finalization. Ends an SHA message-digest operation, writing the
+ * the message digest and zeroing the context. The results are
+ * returned as a raw 20-byte digest, not as the ascii-hex-digits
+ * string form of the digest.
+ *
+ * @param context the SHA context
+ * @param results string to append the 20-byte SHA digest to
+ * @returns #FALSE if not enough memory to append the digest
+ *
+ */
+dbus_bool_t
+_dbus_sha_final (DBusSHAContext *context,
+ DBusString *results)
+{
+ unsigned char digest[20];
+
+ sha_finish (context, digest);
+
+ if (!_dbus_string_append_len (results, (const char *) digest, 20))
+ return FALSE;
+
+ /* some kind of security paranoia, though it seems pointless
+ * to me given the nonzeroed stuff flying around
+ */
+ _DBUS_ZERO(*context);
+
+ return TRUE;
+}
+
+/**
+ * Computes the ASCII hex-encoded shasum of the given data and
+ * appends it to the output string.
+ *
+ * @param data input data to be hashed
+ * @param ascii_output string to append ASCII shasum to
+ * @returns #FALSE if not enough memory
+ */
+dbus_bool_t
+_dbus_sha_compute (const DBusString *data,
+ DBusString *ascii_output)
+{
+ DBusSHAContext context;
+ DBusString digest;
+
+ _dbus_sha_init (&context);
+
+ _dbus_sha_update (&context, data);
+
+ if (!_dbus_string_init (&digest))
+ return FALSE;
+
+ if (!_dbus_sha_final (&context, &digest))
+ goto error;
+
+ if (!_dbus_string_hex_encode (&digest, 0, ascii_output,
+ _dbus_string_get_length (ascii_output)))
+ goto error;
+
+ _dbus_string_free (&digest);
+
+ return TRUE;
+
+ error:
+ _dbus_string_free (&digest);
+ return FALSE;
+}
+
+/** @} */ /* end of exported functions */
diff --git a/src/3rdparty/libdbus/dbus/dbus-sha.h b/src/3rdparty/libdbus/dbus/dbus-sha.h
new file mode 100644
index 00000000..e0677844
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-sha.h
@@ -0,0 +1,58 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-sha.h SHA-1 implementation
+ *
+ * Copyright (C) 2003 Red Hat Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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
+ *
+ */
+#ifndef DBUS_SHA_H
+#define DBUS_SHA_H
+
+#include <dbus/dbus-macros.h>
+#include <dbus/dbus-errors.h>
+#include <dbus/dbus-string.h>
+
+DBUS_BEGIN_DECLS
+
+typedef struct DBusSHAContext DBusSHAContext;
+
+/**
+ * Struct storing state of the SHA algorithm
+ */
+struct DBusSHAContext
+{
+ dbus_uint32_t digest[5]; /**< Message digest */
+ dbus_uint32_t count_lo; /**< 64-bit bit count */
+ dbus_uint32_t count_hi; /**< No clue */
+ dbus_uint32_t data[16]; /**< SHA data buffer */
+};
+
+void _dbus_sha_init (DBusSHAContext *context);
+void _dbus_sha_update (DBusSHAContext *context,
+ const DBusString *data);
+dbus_bool_t _dbus_sha_final (DBusSHAContext *context,
+ DBusString *results);
+DBUS_EMBEDDED_TESTS_EXPORT
+dbus_bool_t _dbus_sha_compute (const DBusString *data,
+ DBusString *ascii_output);
+
+DBUS_END_DECLS
+
+#endif /* DBUS_SHA_H */
diff --git a/src/3rdparty/libdbus/dbus/dbus-shared.h b/src/3rdparty/libdbus/dbus/dbus-shared.h
new file mode 100644
index 00000000..87c0bd84
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-shared.h
@@ -0,0 +1,138 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-shared.h Stuff used by both dbus/dbus.h low-level and C/C++ binding APIs
+ *
+ * Copyright (C) 2004 Red Hat, Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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
+ *
+ */
+
+#ifndef DBUS_SHARED_H
+#define DBUS_SHARED_H
+
+/* Don't include anything in here from anywhere else. It's
+ * intended for use by any random library.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#if 0
+} /* avoids confusing emacs indentation */
+#endif
+#endif
+
+/* Normally docs are in .c files, but there isn't a .c file for this. */
+/**
+ * @defgroup DBusShared Shared constants
+ * @ingroup DBus
+ *
+ * @brief Shared header included by both libdbus and C/C++ bindings such as the GLib bindings.
+ *
+ * Usually a C/C++ binding such as the GLib or Qt binding won't want to include dbus.h in its
+ * public headers. However, a few constants and macros may be useful to include; those are
+ * found here and in dbus-protocol.h
+ *
+ * @{
+ */
+
+
+/**
+ * Well-known bus types. See dbus_bus_get().
+ */
+typedef enum
+{
+ DBUS_BUS_SESSION, /**< The login session bus */
+ DBUS_BUS_SYSTEM, /**< The systemwide bus */
+ DBUS_BUS_STARTER /**< The bus that started us, if any */
+} DBusBusType;
+
+/**
+ * Results that a message handler can return.
+ */
+typedef enum
+{
+ DBUS_HANDLER_RESULT_HANDLED, /**< Message has had its effect - no need to run more handlers. */
+ DBUS_HANDLER_RESULT_NOT_YET_HANDLED, /**< Message has not had any effect - see if other handlers want it. */
+ DBUS_HANDLER_RESULT_NEED_MEMORY /**< Need more memory in order to return #DBUS_HANDLER_RESULT_HANDLED or #DBUS_HANDLER_RESULT_NOT_YET_HANDLED. Please try again later with more memory. */
+} DBusHandlerResult;
+
+/* Bus names */
+
+/** The bus name used to talk to the bus itself. */
+#define DBUS_SERVICE_DBUS "org.freedesktop.DBus"
+
+/* Paths */
+/** The object path used to talk to the bus itself. */
+#define DBUS_PATH_DBUS "/org/freedesktop/DBus"
+/** The object path used in local/in-process-generated messages. */
+#define DBUS_PATH_LOCAL "/org/freedesktop/DBus/Local"
+
+/* Interfaces, these #define don't do much other than
+ * catch typos at compile time
+ */
+/** The interface exported by the object with #DBUS_SERVICE_DBUS and #DBUS_PATH_DBUS */
+#define DBUS_INTERFACE_DBUS "org.freedesktop.DBus"
+/** The monitoring interface exported by the dbus-daemon */
+#define DBUS_INTERFACE_MONITORING "org.freedesktop.DBus.Monitoring"
+
+/** The verbose interface exported by the dbus-daemon */
+#define DBUS_INTERFACE_VERBOSE "org.freedesktop.DBus.Verbose"
+/** The interface supported by introspectable objects */
+#define DBUS_INTERFACE_INTROSPECTABLE "org.freedesktop.DBus.Introspectable"
+/** The interface supported by objects with properties */
+#define DBUS_INTERFACE_PROPERTIES "org.freedesktop.DBus.Properties"
+/** The interface supported by most dbus peers */
+#define DBUS_INTERFACE_PEER "org.freedesktop.DBus.Peer"
+
+/** This is a special interface whose methods can only be invoked
+ * by the local implementation (messages from remote apps aren't
+ * allowed to specify this interface).
+ */
+#define DBUS_INTERFACE_LOCAL "org.freedesktop.DBus.Local"
+
+/* Owner flags */
+#define DBUS_NAME_FLAG_ALLOW_REPLACEMENT 0x1 /**< Allow another service to become the primary owner if requested */
+#define DBUS_NAME_FLAG_REPLACE_EXISTING 0x2 /**< Request to replace the current primary owner */
+#define DBUS_NAME_FLAG_DO_NOT_QUEUE 0x4 /**< If we can not become the primary owner do not place us in the queue */
+
+/* Replies to request for a name */
+#define DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER 1 /**< Service has become the primary owner of the requested name */
+#define DBUS_REQUEST_NAME_REPLY_IN_QUEUE 2 /**< Service could not become the primary owner and has been placed in the queue */
+#define DBUS_REQUEST_NAME_REPLY_EXISTS 3 /**< Service is already in the queue */
+#define DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER 4 /**< Service is already the primary owner */
+
+/* Replies to releasing a name */
+#define DBUS_RELEASE_NAME_REPLY_RELEASED 1 /**< Service was released from the given name */
+#define DBUS_RELEASE_NAME_REPLY_NON_EXISTENT 2 /**< The given name does not exist on the bus */
+#define DBUS_RELEASE_NAME_REPLY_NOT_OWNER 3 /**< Service is not an owner of the given name */
+
+/* Replies to service starts */
+#define DBUS_START_REPLY_SUCCESS 1 /**< Service was auto started */
+#define DBUS_START_REPLY_ALREADY_RUNNING 2 /**< Service was already running */
+
+/** @} */
+
+#ifdef __cplusplus
+#if 0
+{ /* avoids confusing emacs indentation */
+#endif
+}
+#endif
+
+#endif /* DBUS_SHARED_H */
diff --git a/src/3rdparty/libdbus/dbus/dbus-signature.c b/src/3rdparty/libdbus/dbus/dbus-signature.c
new file mode 100644
index 00000000..85d4fc49
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-signature.c
@@ -0,0 +1,417 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-signature.c Routines for reading recursive type signatures
+ *
+ * Copyright (C) 2005 Red Hat, Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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-signature.h"
+#include "dbus-marshal-recursive.h"
+#include "dbus-marshal-basic.h"
+#include "dbus-internals.h"
+#include "dbus-test.h"
+#include <dbus/dbus-test-tap.h>
+
+/**
+ * Implementation details of #DBusSignatureIter, all fields are private
+ */
+typedef struct
+{
+ const char *pos; /**< current position in the signature string */
+ unsigned int finished : 1; /**< true if we are at the end iter */
+ unsigned int in_array : 1; /**< true if we are a subiterator pointing to an array's element type */
+} DBusSignatureRealIter;
+
+_DBUS_STATIC_ASSERT (sizeof (DBusSignatureIter) >= sizeof (DBusSignatureRealIter));
+
+/** macro that checks whether a typecode is a container type */
+#define TYPE_IS_CONTAINER(typecode) \
+ ((typecode) == DBUS_TYPE_STRUCT || \
+ (typecode) == DBUS_TYPE_DICT_ENTRY || \
+ (typecode) == DBUS_TYPE_VARIANT || \
+ (typecode) == DBUS_TYPE_ARRAY)
+
+
+/**
+ * @defgroup DBusSignature Type signature parsing
+ * @ingroup DBus
+ * @brief Parsing D-Bus type signatures
+ * @{
+ */
+
+/**
+ * Initializes a #DBusSignatureIter for reading a type signature. This
+ * function is not safe to use on invalid signatures; be sure to
+ * validate potentially invalid signatures with dbus_signature_validate
+ * before using this function.
+ *
+ * @param iter pointer to an iterator to initialize
+ * @param signature the type signature
+ */
+void
+dbus_signature_iter_init (DBusSignatureIter *iter,
+ const char *signature)
+{
+ DBusSignatureRealIter *real_iter = (DBusSignatureRealIter *) iter;
+
+ real_iter->pos = signature;
+ real_iter->finished = FALSE;
+ real_iter->in_array = FALSE;
+}
+
+/**
+ * Returns the current type pointed to by the iterator.
+ * If the iterator is pointing at a type code such as 's', then
+ * it will be returned directly.
+ *
+ * However, when the parser encounters a container type start
+ * character such as '(' for a structure, the corresponding type for
+ * the container will be returned, e.g. DBUS_TYPE_STRUCT, not '('.
+ * In this case, you should initialize a sub-iterator with
+ * dbus_signature_iter_recurse() to parse the container type.
+ *
+ * @param iter pointer to an iterator
+ * @returns current type (e.g. #DBUS_TYPE_STRING, #DBUS_TYPE_ARRAY)
+ */
+int
+dbus_signature_iter_get_current_type (const DBusSignatureIter *iter)
+{
+ DBusSignatureRealIter *real_iter = (DBusSignatureRealIter *) iter;
+
+ return _dbus_first_type_in_signature_c_str (real_iter->pos, 0);
+}
+
+/**
+ * Returns the signature of the single complete type starting at the
+ * given iterator.
+ *
+ * For example, if the iterator is pointing at the start of "(ii)ii"
+ * (which is "a struct of two ints, followed by an int, followed by an
+ * int"), then "(ii)" would be returned. If the iterator is pointing at
+ * one of the "i" then just that "i" would be returned.
+ *
+ * @param iter pointer to an iterator
+ * @returns current signature; or #NULL if no memory. Should be freed with dbus_free()
+ */
+char *
+dbus_signature_iter_get_signature (const DBusSignatureIter *iter)
+{
+ DBusSignatureRealIter *real_iter = (DBusSignatureRealIter *) iter;
+ DBusString str;
+ char *ret;
+ int pos;
+
+ if (!_dbus_string_init (&str))
+ return NULL;
+
+ pos = 0;
+ _dbus_type_signature_next (real_iter->pos, &pos);
+
+ if (!_dbus_string_append_len (&str, real_iter->pos, pos))
+ return NULL;
+ if (!_dbus_string_steal_data (&str, &ret))
+ ret = NULL;
+ _dbus_string_free (&str);
+
+ return ret;
+}
+
+/**
+ * Convenience function for returning the element type of an array;
+ * This function allows you to avoid initializing a sub-iterator and
+ * getting its current type.
+ *
+ * Undefined behavior results if you invoke this function when the
+ * current type of the iterator is not #DBUS_TYPE_ARRAY.
+ *
+ * @param iter pointer to an iterator
+ * @returns current array element type
+ */
+int
+dbus_signature_iter_get_element_type (const DBusSignatureIter *iter)
+{
+ DBusSignatureRealIter *real_iter = (DBusSignatureRealIter *) iter;
+
+ _dbus_return_val_if_fail (dbus_signature_iter_get_current_type (iter) == DBUS_TYPE_ARRAY, DBUS_TYPE_INVALID);
+
+ return _dbus_first_type_in_signature_c_str (real_iter->pos, 1);
+}
+
+/**
+ * Skip to the next value on this "level". e.g. the next field in a
+ * struct, the next value in an array. Returns #FALSE at the end of the
+ * current container.
+ *
+ * @param iter the iterator
+ * @returns FALSE if nothing more to read at or below this level
+ */
+dbus_bool_t
+dbus_signature_iter_next (DBusSignatureIter *iter)
+{
+ DBusSignatureRealIter *real_iter = (DBusSignatureRealIter *) iter;
+
+ if (real_iter->finished)
+ return FALSE;
+ else
+ {
+ int pos;
+
+ if (real_iter->in_array)
+ {
+ real_iter->finished = TRUE;
+ return FALSE;
+ }
+
+ pos = 0;
+ _dbus_type_signature_next (real_iter->pos, &pos);
+ real_iter->pos += pos;
+
+ if (*real_iter->pos == DBUS_STRUCT_END_CHAR
+ || *real_iter->pos == DBUS_DICT_ENTRY_END_CHAR)
+ {
+ real_iter->finished = TRUE;
+ return FALSE;
+ }
+
+ return *real_iter->pos != DBUS_TYPE_INVALID;
+ }
+}
+
+/**
+ * Initialize a new iterator pointing to the first type in the current
+ * container.
+ *
+ * The results are undefined when calling this if the current type is
+ * a non-container (i.e. if dbus_type_is_container() returns #FALSE
+ * for the result of dbus_signature_iter_get_current_type()).
+ *
+ * @param iter the current interator
+ * @param subiter an iterator to initialize pointing to the first child
+ */
+void
+dbus_signature_iter_recurse (const DBusSignatureIter *iter,
+ DBusSignatureIter *subiter)
+{
+ DBusSignatureRealIter *real_iter = (DBusSignatureRealIter *) iter;
+ DBusSignatureRealIter *real_sub_iter = (DBusSignatureRealIter *) subiter;
+
+ _dbus_return_if_fail (dbus_type_is_container (dbus_signature_iter_get_current_type (iter)));
+
+ *real_sub_iter = *real_iter;
+ real_sub_iter->in_array = FALSE;
+ real_sub_iter->pos++;
+
+ if (dbus_signature_iter_get_current_type (iter) == DBUS_TYPE_ARRAY)
+ real_sub_iter->in_array = TRUE;
+}
+
+/**
+ * Check a type signature for validity. Remember that #NULL can always
+ * be passed instead of a DBusError*, if you don't care about having
+ * an error name and message.
+ *
+ * @param signature a potentially invalid type signature
+ * @param error error return
+ * @returns #TRUE if signature is valid or #FALSE if an error is set
+ */
+dbus_bool_t
+dbus_signature_validate (const char *signature,
+ DBusError *error)
+
+{
+ DBusString str;
+ DBusValidity reason;
+
+ _dbus_string_init_const (&str, signature);
+ reason = _dbus_validate_signature_with_reason (&str, 0, _dbus_string_get_length (&str));
+
+ if (reason == DBUS_VALID)
+ return TRUE;
+ else
+ {
+ dbus_set_error (error, DBUS_ERROR_INVALID_SIGNATURE, "%s",
+ _dbus_validity_to_error_message (reason));
+ return FALSE;
+ }
+}
+
+/**
+ * Check that a type signature is both valid and contains exactly one
+ * complete type. "One complete type" means a single basic type,
+ * array, struct, or dictionary, though the struct or array may be
+ * arbitrarily recursive and complex. More than one complete type
+ * would mean for example "ii" or two integers in sequence.
+ *
+ * @param signature a potentially invalid type signature
+ * @param error error return
+ * @returns #TRUE if signature is valid and has exactly one complete type
+ */
+dbus_bool_t
+dbus_signature_validate_single (const char *signature,
+ DBusError *error)
+{
+ DBusSignatureIter iter;
+
+ if (!dbus_signature_validate (signature, error))
+ return FALSE;
+
+ dbus_signature_iter_init (&iter, signature);
+ if (dbus_signature_iter_get_current_type (&iter) == DBUS_TYPE_INVALID)
+ goto lose;
+ if (!dbus_signature_iter_next (&iter))
+ return TRUE;
+ lose:
+ dbus_set_error (error, DBUS_ERROR_INVALID_SIGNATURE, "Exactly one complete type required in signature");
+ return FALSE;
+}
+
+/**
+ * A "container type" can contain basic types, or nested
+ * container types. #DBUS_TYPE_INVALID is not a container type.
+ *
+ * It is an error to pass an invalid type-code, other than DBUS_TYPE_INVALID,
+ * to this function. The valid type-codes are defined by dbus-protocol.h
+ * and can be checked with dbus_type_is_valid().
+ *
+ * @param typecode either a valid type-code or DBUS_TYPE_INVALID
+ * @returns #TRUE if type is a container
+ */
+dbus_bool_t
+dbus_type_is_container (int typecode)
+{
+ /* only reasonable (non-line-noise) typecodes are allowed */
+ _dbus_return_val_if_fail (dbus_type_is_valid (typecode) || typecode == DBUS_TYPE_INVALID,
+ FALSE);
+ return TYPE_IS_CONTAINER (typecode);
+}
+
+/**
+ * A "basic type" is a somewhat arbitrary concept, but the intent is
+ * to include those types that are fully-specified by a single
+ * typecode, with no additional type information or nested values. So
+ * all numbers and strings are basic types and structs, arrays, and
+ * variants are not basic types. #DBUS_TYPE_INVALID is not a basic
+ * type.
+ *
+ * It is an error to pass an invalid type-code, other than DBUS_TYPE_INVALID,
+ * to this function. The valid type-codes are defined by dbus-protocol.h
+ * and can be checked with dbus_type_is_valid().
+ *
+ * @param typecode either a valid type-code or DBUS_TYPE_INVALID
+ * @returns #TRUE if type is basic
+ */
+dbus_bool_t
+dbus_type_is_basic (int typecode)
+{
+ /* only reasonable (non-line-noise) typecodes are allowed */
+ _dbus_return_val_if_fail (dbus_type_is_valid (typecode) || typecode == DBUS_TYPE_INVALID,
+ FALSE);
+
+ /* everything that isn't invalid or a container */
+ return !(typecode == DBUS_TYPE_INVALID || TYPE_IS_CONTAINER (typecode));
+}
+
+/**
+ * Tells you whether values of this type can change length if you set
+ * them to some other value. For this purpose, you assume that the
+ * first byte of the old and new value would be in the same location,
+ * so alignment padding is not a factor.
+ *
+ * This function is useful to determine whether
+ * dbus_message_iter_get_fixed_array() may be used.
+ *
+ * Some structs are fixed-size (if they contain only fixed-size types)
+ * but struct is not considered a fixed type for purposes of this
+ * function.
+ *
+ * It is an error to pass an invalid type-code, other than DBUS_TYPE_INVALID,
+ * to this function. The valid type-codes are defined by dbus-protocol.h
+ * and can be checked with dbus_type_is_valid().
+ *
+ * @param typecode either a valid type-code or DBUS_TYPE_INVALID
+ * @returns #FALSE if the type can occupy different lengths
+ */
+dbus_bool_t
+dbus_type_is_fixed (int typecode)
+{
+ /* only reasonable (non-line-noise) typecodes are allowed */
+ _dbus_return_val_if_fail (dbus_type_is_valid (typecode) || typecode == DBUS_TYPE_INVALID,
+ FALSE);
+
+ switch (typecode)
+ {
+ case DBUS_TYPE_BYTE:
+ case DBUS_TYPE_BOOLEAN:
+ case DBUS_TYPE_INT16:
+ case DBUS_TYPE_UINT16:
+ case DBUS_TYPE_INT32:
+ case DBUS_TYPE_UINT32:
+ case DBUS_TYPE_INT64:
+ case DBUS_TYPE_UINT64:
+ case DBUS_TYPE_DOUBLE:
+ case DBUS_TYPE_UNIX_FD:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+/**
+ * Return #TRUE if the argument is a valid typecode.
+ * #DBUS_TYPE_INVALID surprisingly enough is not considered valid, and
+ * random unknown bytes aren't either. This function is safe with
+ * untrusted data.
+ *
+ * @param typecode a potential type-code
+ * @returns #TRUE if valid
+ */
+dbus_bool_t
+dbus_type_is_valid (int typecode)
+{
+ switch (typecode)
+ {
+ case DBUS_TYPE_BYTE:
+ case DBUS_TYPE_BOOLEAN:
+ case DBUS_TYPE_INT16:
+ case DBUS_TYPE_UINT16:
+ case DBUS_TYPE_INT32:
+ case DBUS_TYPE_UINT32:
+ case DBUS_TYPE_INT64:
+ case DBUS_TYPE_UINT64:
+ case DBUS_TYPE_DOUBLE:
+ case DBUS_TYPE_STRING:
+ case DBUS_TYPE_OBJECT_PATH:
+ case DBUS_TYPE_SIGNATURE:
+ case DBUS_TYPE_ARRAY:
+ case DBUS_TYPE_STRUCT:
+ case DBUS_TYPE_DICT_ENTRY:
+ case DBUS_TYPE_VARIANT:
+ case DBUS_TYPE_UNIX_FD:
+ return TRUE;
+
+ default:
+ return FALSE;
+ }
+}
+
+/** @} */ /* end of DBusSignature group */
diff --git a/src/3rdparty/libdbus/dbus/dbus-signature.h b/src/3rdparty/libdbus/dbus/dbus-signature.h
new file mode 100644
index 00000000..a8a0486b
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-signature.h
@@ -0,0 +1,97 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-signatures.h utility functions for D-Bus types
+ *
+ * Copyright (C) 2005 Red Hat Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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
+ *
+ */
+#if !defined (DBUS_INSIDE_DBUS_H) && !defined (DBUS_COMPILATION)
+#error "Only <dbus/dbus.h> can be included directly, this file may disappear or change contents."
+#endif
+
+#ifndef DBUS_SIGNATURES_H
+#define DBUS_SIGNATURES_H
+
+#include <dbus/dbus-macros.h>
+#include <dbus/dbus-types.h>
+#include <dbus/dbus-errors.h>
+
+DBUS_BEGIN_DECLS
+
+/**
+ * @addtogroup DBusSignature
+ * @{
+ */
+
+/**
+ * DBusSignatureIter struct; contains no public fields
+ */
+typedef struct
+{
+ void *dummy1; /**< Don't use this */
+ void *dummy2; /**< Don't use this */
+ dbus_uint32_t dummy8; /**< Don't use this */
+ int dummy12; /**< Don't use this */
+ int dummy17; /**< Don't use this */
+} DBusSignatureIter;
+
+DBUS_EXPORT
+void dbus_signature_iter_init (DBusSignatureIter *iter,
+ const char *signature);
+
+DBUS_EXPORT
+int dbus_signature_iter_get_current_type (const DBusSignatureIter *iter);
+
+DBUS_EXPORT
+char * dbus_signature_iter_get_signature (const DBusSignatureIter *iter);
+
+DBUS_EXPORT
+int dbus_signature_iter_get_element_type (const DBusSignatureIter *iter);
+
+DBUS_EXPORT
+dbus_bool_t dbus_signature_iter_next (DBusSignatureIter *iter);
+
+DBUS_EXPORT
+void dbus_signature_iter_recurse (const DBusSignatureIter *iter,
+ DBusSignatureIter *subiter);
+
+DBUS_EXPORT
+dbus_bool_t dbus_signature_validate (const char *signature,
+ DBusError *error);
+
+DBUS_EXPORT
+dbus_bool_t dbus_signature_validate_single (const char *signature,
+ DBusError *error);
+
+DBUS_EXPORT
+dbus_bool_t dbus_type_is_valid (int typecode);
+
+DBUS_EXPORT
+dbus_bool_t dbus_type_is_basic (int typecode);
+DBUS_EXPORT
+dbus_bool_t dbus_type_is_container (int typecode);
+DBUS_EXPORT
+dbus_bool_t dbus_type_is_fixed (int typecode);
+
+/** @} */
+
+DBUS_END_DECLS
+
+#endif /* DBUS_SIGNATURE_H */
diff --git a/src/3rdparty/libdbus/dbus/dbus-sockets-win.h b/src/3rdparty/libdbus/dbus/dbus-sockets-win.h
new file mode 100644
index 00000000..0924179c
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-sockets-win.h
@@ -0,0 +1,55 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-sockets.h Wrappers around socket features (internal to D-BUS implementation)
+ *
+ * Copyright (C) 2005 Novell, Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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
+ *
+ */
+
+#ifndef DBUS_SOCKETS_H
+#define DBUS_SOCKETS_H
+
+#if defined(DBUS_WIN) || defined(DBUS_WINCE)
+
+
+
+#ifndef STRICT
+#define STRICT
+#include <winsock2.h>
+#undef STRICT
+#endif
+#include <winsock2.h>
+
+#undef interface
+
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#define DBUS_SOCKET_API_RETURNS_ERROR(n) ((n) == SOCKET_ERROR)
+#define DBUS_SOCKET_SET_ERRNO() (_dbus_win_set_errno (WSAGetLastError()))
+
+#else
+
+#error "dbus-sockets-win.h should not be included on non-Windows"
+
+#endif /* !Win32 */
+
+#endif /* DBUS_SOCKETS_H */
diff --git a/src/3rdparty/libdbus/dbus/dbus-string-private.h b/src/3rdparty/libdbus/dbus/dbus-string-private.h
new file mode 100644
index 00000000..24e1e610
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-string-private.h
@@ -0,0 +1,135 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-string-private.h String utility class (internal to D-Bus implementation)
+ *
+ * Copyright (C) 2002, 2003 Red Hat, Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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
+ *
+ */
+
+#ifndef DBUS_STRING_PRIVATE_H
+#define DBUS_STRING_PRIVATE_H
+
+#include <dbus/dbus-internals.h>
+#include <dbus/dbus-memory.h>
+#include <dbus/dbus-types.h>
+
+#ifndef DBUS_CAN_USE_DBUS_STRING_PRIVATE
+#error "Don't go including dbus-string-private.h for no good reason"
+#endif
+
+DBUS_BEGIN_DECLS
+
+/**
+ * @brief Internals of DBusString.
+ *
+ * DBusString internals. DBusString is an opaque objects, it must be
+ * used via accessor functions.
+ */
+typedef struct
+{
+ unsigned char *str; /**< String data, plus nul termination */
+ int len; /**< Length without nul */
+ int allocated; /**< Allocated size of data */
+ unsigned int constant : 1; /**< String data is not owned by DBusString */
+ unsigned int locked : 1; /**< DBusString has been locked and can't be changed */
+ unsigned int valid : 1; /**< DBusString is valid (initialized and not freed) */
+ unsigned int align_offset : 3; /**< str - align_offset is the actual malloc block */
+} DBusRealString;
+
+_DBUS_STATIC_ASSERT (sizeof (DBusRealString) == sizeof (DBusString));
+
+/**
+ * @defgroup DBusStringInternals DBusString implementation details
+ * @ingroup DBusInternals
+ * @brief DBusString implementation details
+ *
+ * The guts of DBusString.
+ *
+ * @{
+ */
+
+/**
+ * The maximum length of a DBusString
+ */
+#define _DBUS_STRING_MAX_LENGTH (_DBUS_INT32_MAX - _DBUS_STRING_ALLOCATION_PADDING)
+
+/**
+ * Checks a bunch of assertions about a string object
+ *
+ * @param real the DBusRealString
+ */
+#define DBUS_GENERIC_STRING_PREAMBLE(real) \
+ do { \
+ (void) real; /* might be unused unless asserting */ \
+ _dbus_assert ((real) != NULL); \
+ _dbus_assert ((real)->valid); \
+ _dbus_assert ((real)->len >= 0); \
+ _dbus_assert ((real)->allocated >= 0); \
+ _dbus_assert ((real)->len <= ((real)->allocated - _DBUS_STRING_ALLOCATION_PADDING)); \
+ _dbus_assert ((real)->len <= _DBUS_STRING_MAX_LENGTH); \
+ } while (0)
+
+/**
+ * Checks assertions about a string object that needs to be
+ * modifiable - may not be locked or const. Also declares
+ * the "real" variable pointing to DBusRealString.
+ * @param str the string
+ */
+#define DBUS_STRING_PREAMBLE(str) DBusRealString *real = (DBusRealString*) str; \
+ DBUS_GENERIC_STRING_PREAMBLE (real); \
+ _dbus_assert (!(real)->constant); \
+ _dbus_assert (!(real)->locked)
+
+/**
+ * Checks assertions about a string object that may be locked but
+ * can't be const. i.e. a string object that we can free. Also
+ * declares the "real" variable pointing to DBusRealString.
+ *
+ * @param str the string
+ */
+#define DBUS_LOCKED_STRING_PREAMBLE(str) DBusRealString *real = (DBusRealString*) str; \
+ DBUS_GENERIC_STRING_PREAMBLE (real); \
+ _dbus_assert (!(real)->constant)
+
+/**
+ * Checks assertions about a string that may be const or locked. Also
+ * declares the "real" variable pointing to DBusRealString.
+ * @param str the string.
+ */
+#define DBUS_CONST_STRING_PREAMBLE(str) const DBusRealString *real = (DBusRealString*) str; \
+ DBUS_GENERIC_STRING_PREAMBLE (real)
+
+/**
+ * Checks for ASCII blank byte
+ * @param c the byte
+ */
+#define DBUS_IS_ASCII_BLANK(c) ((c) == ' ' || (c) == '\t')
+
+/**
+ * Checks for ASCII whitespace byte
+ * @param c the byte
+ */
+#define DBUS_IS_ASCII_WHITE(c) ((c) == ' ' || (c) == '\t' || (c) == '\n' || (c) == '\r')
+
+/** @} */
+
+DBUS_END_DECLS
+
+#endif /* DBUS_STRING_PRIVATE_H */
diff --git a/src/3rdparty/libdbus/dbus/dbus-string.c b/src/3rdparty/libdbus/dbus/dbus-string.c
new file mode 100644
index 00000000..29210de7
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-string.c
@@ -0,0 +1,2816 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-string.c String utility class (internal to D-Bus implementation)
+ *
+ * Copyright 2002-2007 Red Hat, Inc.
+ * Copyright 2003 CodeFactory AB
+ * Copyright 2003 Mark McLoughlin
+ * Copyright 2004 Michael Meeks
+ * Copyright 2006-2014 Ralf Habacker <ralf.habacker@freenet.de>
+ * Copyright 2006-2018 Collabora Ltd.
+ * Copyright 2007 Allison Lortie
+ * Copyright 2011 Roberto Guido
+ * Copyright 2013 Chengwei Yang / Intel
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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-string.h"
+/* we allow a system header here, for speed/convenience */
+#include <string.h>
+/* for vsnprintf */
+#include <stdio.h>
+#define DBUS_CAN_USE_DBUS_STRING_PRIVATE 1
+#include "dbus-string-private.h"
+#include "dbus-marshal-basic.h" /* probably should be removed by moving the usage of DBUS_TYPE
+ * into the marshaling-related files
+ */
+
+/**
+ * @defgroup DBusString DBusString class
+ * @ingroup DBusInternals
+ * @brief DBusString data structure for safer string handling
+ *
+ * Types and functions related to DBusString. DBusString is intended
+ * to be a string class that makes it hard to mess up security issues
+ * (and just in general harder to write buggy code). It should be
+ * used (or extended and then used) rather than the libc stuff in
+ * string.h. The string class is a bit inconvenient at spots because
+ * it handles out-of-memory failures and tries to be extra-robust.
+ *
+ * A DBusString has a maximum length set at initialization time; this
+ * can be used to ensure that a buffer doesn't get too big. The
+ * _dbus_string_lengthen() method checks for overflow, and for max
+ * length being exceeded.
+ *
+ * Try to avoid conversion to a plain C string, i.e. add methods on
+ * the string object instead, only convert to C string when passing
+ * things out to the public API. In particular, no sprintf, strcpy,
+ * strcat, any of that should be used. The GString feature of
+ * accepting negative numbers for "length of string" is also absent,
+ * because it could keep us from detecting bogus huge lengths. i.e. if
+ * we passed in some bogus huge length it would be taken to mean
+ * "current length of string" instead of "broken crack"
+ *
+ * @todo #DBusString needs a lot of cleaning up; some of the
+ * API is no longer used, and the API is pretty inconsistent.
+ * In particular all the "append" APIs, especially those involving
+ * alignment but probably lots of them, are no longer used by the
+ * marshaling code which always does "inserts" now.
+ */
+
+/**
+ * @addtogroup DBusString
+ * @{
+ */
+
+static void
+fixup_alignment (DBusRealString *real)
+{
+ unsigned char *aligned;
+ unsigned char *real_block;
+ unsigned int old_align_offset;
+
+ /* we have to have extra space in real->allocated for the align offset and nul byte */
+ _dbus_assert (real->len <= real->allocated - _DBUS_STRING_ALLOCATION_PADDING);
+
+ old_align_offset = real->align_offset;
+ real_block = real->str - old_align_offset;
+
+ aligned = _DBUS_ALIGN_ADDRESS (real_block, 8);
+
+ real->align_offset = aligned - real_block;
+ real->str = aligned;
+
+ if (old_align_offset != real->align_offset)
+ {
+ /* Here comes the suck */
+ memmove (real_block + real->align_offset,
+ real_block + old_align_offset,
+ real->len + 1);
+ }
+
+ _dbus_assert (real->align_offset < 8);
+ _dbus_assert (_DBUS_ALIGN_ADDRESS (real->str, 8) == real->str);
+}
+
+static void
+undo_alignment (DBusRealString *real)
+{
+ if (real->align_offset != 0)
+ {
+ memmove (real->str - real->align_offset,
+ real->str,
+ real->len + 1);
+
+ real->str = real->str - real->align_offset;
+ real->align_offset = 0;
+ }
+}
+
+/**
+ * Initializes a string that can be up to the given allocation size
+ * before it has to realloc. The string starts life with zero length.
+ * The string must eventually be freed with _dbus_string_free().
+ *
+ * @param str memory to hold the string
+ * @param allocate_size amount to preallocate
+ * @returns #TRUE on success, #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_string_init_preallocated (DBusString *str,
+ int allocate_size)
+{
+ DBusRealString *real;
+
+ _DBUS_STATIC_ASSERT (sizeof (DBusString) == sizeof (DBusRealString));
+
+ _dbus_assert (str != NULL);
+
+ real = (DBusRealString*) str;
+
+ /* It's very important not to touch anything
+ * other than real->str if we're going to fail,
+ * since we also use this function to reset
+ * an existing string, e.g. in _dbus_string_steal_data()
+ */
+
+ real->str = dbus_malloc (_DBUS_STRING_ALLOCATION_PADDING + allocate_size);
+ if (real->str == NULL)
+ return FALSE;
+
+ real->allocated = _DBUS_STRING_ALLOCATION_PADDING + allocate_size;
+ real->len = 0;
+ real->str[real->len] = '\0';
+
+ real->constant = FALSE;
+ real->locked = FALSE;
+ real->valid = TRUE;
+ real->align_offset = 0;
+
+ fixup_alignment (real);
+
+ return TRUE;
+}
+
+/**
+ * Initializes a string. The string starts life with zero length. The
+ * string must eventually be freed with _dbus_string_free().
+ *
+ * @param str memory to hold the string
+ * @returns #TRUE on success, #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_string_init (DBusString *str)
+{
+ return _dbus_string_init_preallocated (str, 0);
+}
+
+/**
+ * Initializes a constant string. The value parameter is not copied
+ * (should be static), and the string may never be modified.
+ * It is safe but not necessary to call _dbus_string_free()
+ * on a const string. The string has a length limit of MAXINT - 8.
+ *
+ * @param str memory to use for the string
+ * @param value a string to be stored in str (not copied!!!)
+ */
+void
+_dbus_string_init_const (DBusString *str,
+ const char *value)
+{
+ _dbus_assert (value != NULL);
+
+ _dbus_string_init_const_len (str, value,
+ strlen (value));
+}
+
+/**
+ * Initializes a constant string with a length. The value parameter is
+ * not copied (should be static), and the string may never be
+ * modified. It is safe but not necessary to call _dbus_string_free()
+ * on a const string.
+ *
+ * @param str memory to use for the string
+ * @param value a string to be stored in str (not copied!!!)
+ * @param len the length to use
+ */
+void
+_dbus_string_init_const_len (DBusString *str,
+ const char *value,
+ int len)
+{
+ DBusRealString *real;
+
+ _dbus_assert (str != NULL);
+ _dbus_assert (len == 0 || value != NULL);
+ _dbus_assert (len <= _DBUS_STRING_MAX_LENGTH);
+ _dbus_assert (len >= 0);
+
+ real = (DBusRealString*) str;
+
+ real->str = (unsigned char*) value;
+ real->len = len;
+ real->allocated = real->len + _DBUS_STRING_ALLOCATION_PADDING; /* a lie, just to avoid special-case assertions... */
+ real->constant = TRUE;
+ real->locked = TRUE;
+ real->valid = TRUE;
+ real->align_offset = 0;
+
+ /* We don't require const strings to be 8-byte aligned as the
+ * memory is coming from elsewhere.
+ */
+}
+
+/**
+ * Initializes a string from another string
+ *
+ * The string must be freed with _dbus_string_free() in case of success.
+ * In case of error the string is freed by this function itself.
+ *
+ * @param str memory to hold the string
+ * @param from instance from which the string is initialized
+ * @returns #TRUE on success, #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_string_init_from_string(DBusString *str,
+ const DBusString *from)
+{
+ if (!_dbus_string_init (str))
+ return FALSE;
+ if (!_dbus_string_append (str, _dbus_string_get_const_data (from)))
+ {
+ _dbus_string_free (str);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/**
+ * Frees a string created by _dbus_string_init(), and fills it with the
+ * same contents as #_DBUS_STRING_INIT_INVALID.
+ *
+ * Unlike all other #DBusString API, it is also valid to call this function
+ * for a string that was filled with #_DBUS_STRING_INIT_INVALID.
+ * This is convenient for error rollback.
+ *
+ * @param str memory where the string is stored.
+ */
+void
+_dbus_string_free (DBusString *str)
+{
+ DBusRealString *real = (DBusRealString*) str;
+ /* DBusRealString and DBusString have the same members in the same order,
+ * just differently-named */
+ DBusRealString invalid = _DBUS_STRING_INIT_INVALID;
+
+ /* Allow for the _DBUS_STRING_INIT_INVALID case */
+ if (real->str == NULL && real->len == 0 && real->allocated == 0 &&
+ !real->constant && !real->locked && !real->valid &&
+ real->align_offset == 0)
+ return;
+
+ DBUS_GENERIC_STRING_PREAMBLE (real);
+
+ if (real->constant)
+ goto wipe;
+
+ /* so it's safe if @p str returned by a failed
+ * _dbus_string_init call
+ * Bug: https://bugs.freedesktop.org/show_bug.cgi?id=65959
+ */
+ if (real->str == NULL)
+ goto wipe;
+
+ dbus_free (real->str - real->align_offset);
+
+wipe:
+ *real = invalid;
+ real->valid = FALSE;
+}
+
+static dbus_bool_t
+compact (DBusRealString *real,
+ int max_waste)
+{
+ unsigned char *new_str;
+ int new_allocated;
+ int waste;
+
+ waste = real->allocated - (real->len + _DBUS_STRING_ALLOCATION_PADDING);
+
+ if (waste <= max_waste)
+ return TRUE;
+
+ new_allocated = real->len + _DBUS_STRING_ALLOCATION_PADDING;
+
+ new_str = dbus_realloc (real->str - real->align_offset, new_allocated);
+ if (_DBUS_UNLIKELY (new_str == NULL))
+ return FALSE;
+
+ real->str = new_str + real->align_offset;
+ real->allocated = new_allocated;
+ fixup_alignment (real);
+
+ return TRUE;
+}
+
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
+/* Not using this feature at the moment,
+ * so marked DBUS_ENABLE_EMBEDDED_TESTS-only
+ */
+/**
+ * Locks a string such that any attempts to change the string will
+ * result in aborting the program. Also, if the string is wasting a
+ * lot of memory (allocation is sufficiently larger than what the
+ * string is really using), _dbus_string_lock() will realloc the
+ * string's data to "compact" it.
+ *
+ * @param str the string to lock.
+ */
+void
+_dbus_string_lock (DBusString *str)
+{
+ DBUS_LOCKED_STRING_PREAMBLE (str); /* can lock multiple times */
+
+ real->locked = TRUE;
+
+ /* Try to realloc to avoid excess memory usage, since
+ * we know we won't change the string further
+ */
+#define MAX_WASTE 48
+ compact (real, MAX_WASTE);
+}
+#endif /* DBUS_ENABLE_EMBEDDED_TESTS */
+
+static dbus_bool_t
+reallocate_for_length (DBusRealString *real,
+ int new_length)
+{
+ int new_allocated;
+ unsigned char *new_str;
+
+ /* at least double our old allocation to avoid O(n), avoiding
+ * overflow
+ */
+ if (real->allocated > (_DBUS_STRING_MAX_LENGTH + _DBUS_STRING_ALLOCATION_PADDING) / 2)
+ new_allocated = _DBUS_STRING_MAX_LENGTH + _DBUS_STRING_ALLOCATION_PADDING;
+ else
+ new_allocated = real->allocated * 2;
+
+ /* if you change the code just above here, run the tests without
+ * the following assert-only hack before you commit
+ */
+ /* This is keyed off asserts in addition to tests so when you
+ * disable asserts to profile, you don't get this destroyer
+ * of profiles.
+ */
+#if defined (DBUS_ENABLE_EMBEDDED_TESTS) && !defined (DBUS_DISABLE_ASSERT)
+ new_allocated = 0; /* ensure a realloc every time so that we go
+ * through all malloc failure codepaths
+ */
+#endif
+
+ /* But be sure we always alloc at least space for the new length */
+ new_allocated = MAX (new_allocated,
+ new_length + _DBUS_STRING_ALLOCATION_PADDING);
+
+ _dbus_assert (new_allocated >= real->allocated); /* code relies on this */
+ new_str = dbus_realloc (real->str - real->align_offset, new_allocated);
+ if (_DBUS_UNLIKELY (new_str == NULL))
+ return FALSE;
+
+ real->str = new_str + real->align_offset;
+ real->allocated = new_allocated;
+ fixup_alignment (real);
+
+ return TRUE;
+}
+
+/**
+ * Compacts the string to avoid wasted memory. Wasted memory is
+ * memory that is allocated but not actually required to store the
+ * current length of the string. The compact is only done if more
+ * than the given amount of memory is being wasted (otherwise the
+ * waste is ignored and the call does nothing).
+ *
+ * @param str the string
+ * @param max_waste the maximum amount of waste to ignore
+ * @returns #FALSE if the compact failed due to realloc failure
+ */
+dbus_bool_t
+_dbus_string_compact (DBusString *str,
+ int max_waste)
+{
+ DBUS_STRING_PREAMBLE (str);
+
+ return compact (real, max_waste);
+}
+
+static dbus_bool_t
+set_length (DBusRealString *real,
+ int new_length)
+{
+ /* Note, we are setting the length not including nul termination */
+
+ /* exceeding max length is the same as failure to allocate memory */
+ if (_DBUS_UNLIKELY (new_length > _DBUS_STRING_MAX_LENGTH))
+ return FALSE;
+ else if (new_length > (real->allocated - _DBUS_STRING_ALLOCATION_PADDING) &&
+ _DBUS_UNLIKELY (!reallocate_for_length (real, new_length)))
+ return FALSE;
+ else
+ {
+ real->len = new_length;
+ real->str[new_length] = '\0';
+ return TRUE;
+ }
+}
+
+static dbus_bool_t
+open_gap (int len,
+ DBusRealString *dest,
+ int insert_at)
+{
+ if (len == 0)
+ return TRUE;
+
+ if (len > _DBUS_STRING_MAX_LENGTH - dest->len)
+ return FALSE; /* detected overflow of dest->len + len below */
+
+ if (!set_length (dest, dest->len + len))
+ return FALSE;
+
+ memmove (dest->str + insert_at + len,
+ dest->str + insert_at,
+ dest->len - len - insert_at);
+
+ return TRUE;
+}
+
+/**
+ * Returns the allocated size of the string
+ *
+ * @param str the string
+ * @returns the allocated size
+ */
+int
+_dbus_string_get_allocated_size (const DBusString *str)
+{
+ DBUS_CONST_STRING_PREAMBLE (str);
+
+ return real->allocated;
+}
+
+#ifndef _dbus_string_get_data
+/**
+ * Gets the raw character buffer from the string. The returned buffer
+ * will be nul-terminated, but note that strings may contain binary
+ * data so there may be extra nul characters prior to the termination.
+ * This function should be little-used, extend DBusString or add
+ * stuff to dbus-sysdeps.c instead. It's an error to use this
+ * function on a const string.
+ *
+ * @param str the string
+ * @returns the data
+ */
+char*
+_dbus_string_get_data (DBusString *str)
+{
+ DBUS_STRING_PREAMBLE (str);
+
+ return (char*) real->str;
+}
+#endif /* _dbus_string_get_data */
+
+/* only do the function if we don't have the macro */
+#ifndef _dbus_string_get_const_data
+/**
+ * Gets the raw character buffer from a const string.
+ *
+ * @param str the string
+ * @returns the string data
+ */
+const char*
+_dbus_string_get_const_data (const DBusString *str)
+{
+ DBUS_CONST_STRING_PREAMBLE (str);
+
+ return (const char*) real->str;
+}
+#endif /* _dbus_string_get_const_data */
+
+/**
+ * Gets a sub-portion of the raw character buffer from the
+ * string. The "len" field is required simply for error
+ * checking, to be sure you don't try to use more
+ * string than exists. The nul termination of the
+ * returned buffer remains at the end of the entire
+ * string, not at start + len.
+ *
+ * @param str the string
+ * @param start byte offset to return
+ * @param len length of segment to return
+ * @returns the string data
+ */
+char*
+_dbus_string_get_data_len (DBusString *str,
+ int start,
+ int len)
+{
+ DBUS_STRING_PREAMBLE (str);
+ _dbus_assert (start >= 0);
+ _dbus_assert (len >= 0);
+ _dbus_assert (start <= real->len);
+ _dbus_assert (len <= real->len - start);
+
+ return (char*) real->str + start;
+}
+
+/* only do the function if we don't have the macro */
+#ifndef _dbus_string_get_const_data_len
+/**
+ * const version of _dbus_string_get_data_len().
+ *
+ * @param str the string
+ * @param start byte offset to return
+ * @param len length of segment to return
+ * @returns the string data
+ */
+const char*
+_dbus_string_get_const_data_len (const DBusString *str,
+ int start,
+ int len)
+{
+ DBUS_CONST_STRING_PREAMBLE (str);
+ _dbus_assert (start >= 0);
+ _dbus_assert (len >= 0);
+ _dbus_assert (start <= real->len);
+ _dbus_assert (len <= real->len - start);
+
+ return (const char*) real->str + start;
+}
+#endif /* _dbus_string_get_const_data_len */
+
+/* only do the function if we don't have the macro */
+#ifndef _dbus_string_set_byte
+/**
+ * Sets the value of the byte at the given position.
+ *
+ * @param str the string
+ * @param i the position
+ * @param byte the new value
+ */
+void
+_dbus_string_set_byte (DBusString *str,
+ int i,
+ unsigned char byte)
+{
+ DBUS_STRING_PREAMBLE (str);
+ _dbus_assert (i < real->len);
+ _dbus_assert (i >= 0);
+
+ real->str[i] = byte;
+}
+#endif /* _dbus_string_set_byte */
+
+/* only have the function if we didn't create a macro */
+#ifndef _dbus_string_get_byte
+/**
+ * Gets the byte at the given position. It is
+ * allowed to ask for the nul byte at the end of
+ * the string.
+ *
+ * @param str the string
+ * @param start the position
+ * @returns the byte at that position
+ */
+unsigned char
+_dbus_string_get_byte (const DBusString *str,
+ int start)
+{
+ DBUS_CONST_STRING_PREAMBLE (str);
+ _dbus_assert (start <= real->len);
+ _dbus_assert (start >= 0);
+
+ return real->str[start];
+}
+#endif /* _dbus_string_get_byte */
+
+/**
+ * Inserts a number of bytes of a given value at the
+ * given position.
+ *
+ * @param str the string
+ * @param i the position
+ * @param n_bytes number of bytes
+ * @param byte the value to insert
+ * @returns #TRUE on success
+ */
+dbus_bool_t
+_dbus_string_insert_bytes (DBusString *str,
+ int i,
+ int n_bytes,
+ unsigned char byte)
+{
+ DBUS_STRING_PREAMBLE (str);
+ _dbus_assert (i <= real->len);
+ _dbus_assert (i >= 0);
+ _dbus_assert (n_bytes >= 0);
+
+ if (n_bytes == 0)
+ return TRUE;
+
+ if (!open_gap (n_bytes, real, i))
+ return FALSE;
+
+ memset (real->str + i, byte, n_bytes);
+
+ return TRUE;
+}
+
+/**
+ * Inserts a single byte at the given position.
+ *
+ * @param str the string
+ * @param i the position
+ * @param byte the value to insert
+ * @returns #TRUE on success
+ */
+dbus_bool_t
+_dbus_string_insert_byte (DBusString *str,
+ int i,
+ unsigned char byte)
+{
+ DBUS_STRING_PREAMBLE (str);
+ _dbus_assert (i <= real->len);
+ _dbus_assert (i >= 0);
+
+ if (!open_gap (1, real, i))
+ return FALSE;
+
+ real->str[i] = byte;
+
+ return TRUE;
+}
+
+/**
+ * Like _dbus_string_get_data(), but removes the
+ * gotten data from the original string. The caller
+ * must free the data returned. This function may
+ * fail due to lack of memory, and return #FALSE.
+ *
+ * @param str the string
+ * @param data_return location to return the buffer
+ * @returns #TRUE on success
+ */
+dbus_bool_t
+_dbus_string_steal_data (DBusString *str,
+ char **data_return)
+{
+ DBUS_STRING_PREAMBLE (str);
+ _dbus_assert (data_return != NULL);
+
+ undo_alignment (real);
+
+ *data_return = (char*) real->str;
+
+ /* reset the string */
+ if (!_dbus_string_init (str))
+ {
+ /* hrm, put it back then */
+ real->str = (unsigned char*) *data_return;
+ *data_return = NULL;
+ fixup_alignment (real);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**
+ * Copies the data from the string into a char*
+ *
+ * @param str the string
+ * @param data_return place to return the data
+ * @returns #TRUE on success, #FALSE on no memory
+ */
+dbus_bool_t
+_dbus_string_copy_data (const DBusString *str,
+ char **data_return)
+{
+ DBUS_CONST_STRING_PREAMBLE (str);
+ _dbus_assert (data_return != NULL);
+
+ *data_return = dbus_malloc (real->len + 1);
+ if (*data_return == NULL)
+ return FALSE;
+
+ memcpy (*data_return, real->str, real->len + 1);
+
+ return TRUE;
+}
+
+/**
+ * Copies the contents of a DBusString into a different buffer. It is
+ * a bug if avail_len is too short to hold the string contents. nul
+ * termination is not copied, just the supplied bytes.
+ *
+ * @param str a string
+ * @param buffer a C buffer to copy data to
+ * @param avail_len maximum length of C buffer
+ */
+void
+_dbus_string_copy_to_buffer (const DBusString *str,
+ char *buffer,
+ int avail_len)
+{
+ DBUS_CONST_STRING_PREAMBLE (str);
+
+ _dbus_assert (avail_len >= 0);
+ _dbus_assert (avail_len >= real->len);
+
+ memcpy (buffer, real->str, real->len);
+}
+
+/**
+ * Copies the contents of a DBusString into a different buffer. It is
+ * a bug if avail_len is too short to hold the string contents plus a
+ * nul byte.
+ *
+ * @param str a string
+ * @param buffer a C buffer to copy data to
+ * @param avail_len maximum length of C buffer
+ */
+void
+_dbus_string_copy_to_buffer_with_nul (const DBusString *str,
+ char *buffer,
+ int avail_len)
+{
+ DBUS_CONST_STRING_PREAMBLE (str);
+
+ _dbus_assert (avail_len >= 0);
+ _dbus_assert (avail_len > real->len);
+
+ memcpy (buffer, real->str, real->len+1);
+}
+
+/* Only have the function if we don't have the macro */
+#ifndef _dbus_string_get_length
+/**
+ * Gets the length of a string (not including nul termination).
+ *
+ * @returns the length.
+ */
+int
+_dbus_string_get_length (const DBusString *str)
+{
+ DBUS_CONST_STRING_PREAMBLE (str);
+
+ return real->len;
+}
+#endif /* !_dbus_string_get_length */
+
+/**
+ * Makes a string longer by the given number of bytes. Checks whether
+ * adding additional_length to the current length would overflow an
+ * integer, and checks for exceeding a string's max length.
+ * The new bytes are not initialized, other than nul-terminating
+ * the end of the string. The uninitialized bytes may contain
+ * nul bytes or other junk.
+ *
+ * @param str a string
+ * @param additional_length length to add to the string.
+ * @returns #TRUE on success.
+ */
+dbus_bool_t
+_dbus_string_lengthen (DBusString *str,
+ int additional_length)
+{
+ DBUS_STRING_PREAMBLE (str);
+ _dbus_assert (additional_length >= 0);
+
+ if (_DBUS_UNLIKELY (additional_length > _DBUS_STRING_MAX_LENGTH - real->len))
+ return FALSE; /* would overflow */
+
+ return set_length (real,
+ real->len + additional_length);
+}
+
+/**
+ * Makes a string shorter by the given number of bytes.
+ *
+ * @param str a string
+ * @param length_to_remove length to remove from the string.
+ */
+void
+_dbus_string_shorten (DBusString *str,
+ int length_to_remove)
+{
+ DBUS_STRING_PREAMBLE (str);
+ _dbus_assert (length_to_remove >= 0);
+ _dbus_assert (length_to_remove <= real->len);
+
+ set_length (real,
+ real->len - length_to_remove);
+}
+
+/**
+ * Sets the length of a string. Can be used to truncate or lengthen
+ * the string. If the string is lengthened, the function may fail and
+ * return #FALSE. Newly-added bytes are not initialized, as with
+ * _dbus_string_lengthen().
+ *
+ * @param str a string
+ * @param length new length of the string.
+ * @returns #FALSE on failure.
+ */
+dbus_bool_t
+_dbus_string_set_length (DBusString *str,
+ int length)
+{
+ DBUS_STRING_PREAMBLE (str);
+ _dbus_assert (length >= 0);
+
+ return set_length (real, length);
+}
+
+static dbus_bool_t
+align_insert_point_then_open_gap (DBusString *str,
+ int *insert_at_p,
+ int alignment,
+ int gap_size)
+{
+ unsigned long new_len; /* ulong to avoid _DBUS_ALIGN_VALUE overflow */
+ unsigned long gap_pos;
+ int insert_at;
+ int delta;
+ DBUS_STRING_PREAMBLE (str);
+ _dbus_assert (alignment >= 1);
+ _dbus_assert (alignment <= 8); /* it has to be a bug if > 8 */
+
+ insert_at = *insert_at_p;
+
+ _dbus_assert (insert_at <= real->len);
+
+ gap_pos = _DBUS_ALIGN_VALUE (insert_at, alignment);
+ new_len = real->len + (gap_pos - insert_at) + gap_size;
+
+ if (_DBUS_UNLIKELY (new_len > (unsigned long) _DBUS_STRING_MAX_LENGTH))
+ return FALSE;
+
+ delta = new_len - real->len;
+ _dbus_assert (delta >= 0);
+
+ if (delta == 0) /* only happens if gap_size == 0 and insert_at is aligned already */
+ {
+ _dbus_assert (((unsigned long) *insert_at_p) == gap_pos);
+ return TRUE;
+ }
+
+ if (_DBUS_UNLIKELY (!open_gap (new_len - real->len,
+ real, insert_at)))
+ return FALSE;
+
+ /* nul the padding if we had to add any padding */
+ if (gap_size < delta)
+ {
+ memset (&real->str[insert_at], '\0',
+ gap_pos - insert_at);
+ }
+
+ *insert_at_p = gap_pos;
+
+ return TRUE;
+}
+
+static dbus_bool_t
+align_length_then_lengthen (DBusString *str,
+ int alignment,
+ int then_lengthen_by)
+{
+ int insert_at;
+
+ insert_at = _dbus_string_get_length (str);
+
+ return align_insert_point_then_open_gap (str,
+ &insert_at,
+ alignment, then_lengthen_by);
+}
+
+/**
+ * Align the length of a string to a specific alignment (typically 4 or 8)
+ * by appending nul bytes to the string.
+ *
+ * @param str a string
+ * @param alignment the alignment
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_string_align_length (DBusString *str,
+ int alignment)
+{
+ return align_length_then_lengthen (str, alignment, 0);
+}
+
+/**
+ * Preallocate extra_bytes such that a future lengthening of the
+ * string by extra_bytes is guaranteed to succeed without an out of
+ * memory error.
+ *
+ * @param str a string
+ * @param extra_bytes bytes to alloc
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_string_alloc_space (DBusString *str,
+ int extra_bytes)
+{
+ if (!_dbus_string_lengthen (str, extra_bytes))
+ return FALSE;
+ _dbus_string_shorten (str, extra_bytes);
+
+ return TRUE;
+}
+
+static dbus_bool_t
+append (DBusRealString *real,
+ const char *buffer,
+ int buffer_len)
+{
+ if (buffer_len == 0)
+ return TRUE;
+
+ if (!_dbus_string_lengthen ((DBusString*)real, buffer_len))
+ return FALSE;
+
+ memcpy (real->str + (real->len - buffer_len),
+ buffer,
+ buffer_len);
+
+ return TRUE;
+}
+
+/**
+ * Appends a nul-terminated C-style string to a DBusString.
+ *
+ * @param str the DBusString
+ * @param buffer the nul-terminated characters to append
+ * @returns #FALSE if not enough memory.
+ */
+dbus_bool_t
+_dbus_string_append (DBusString *str,
+ const char *buffer)
+{
+ unsigned long buffer_len;
+
+ DBUS_STRING_PREAMBLE (str);
+ _dbus_assert (buffer != NULL);
+
+ buffer_len = strlen (buffer);
+ if (buffer_len > (unsigned long) _DBUS_STRING_MAX_LENGTH)
+ return FALSE;
+
+ return append (real, buffer, buffer_len);
+}
+
+/**
+ * Inserts 2 bytes aligned on a 2 byte boundary
+ * with any alignment padding initialized to 0.
+ *
+ * @param str the DBusString
+ * @param insert_at where to insert
+ * @param octets 2 bytes to insert
+ * @returns #FALSE if not enough memory.
+ */
+dbus_bool_t
+_dbus_string_insert_2_aligned (DBusString *str,
+ int insert_at,
+ const unsigned char octets[2])
+{
+ DBUS_STRING_PREAMBLE (str);
+
+ if (!align_insert_point_then_open_gap (str, &insert_at, 2, 2))
+ return FALSE;
+
+ memcpy (real->str + insert_at, octets, 2);
+
+ return TRUE;
+}
+
+/**
+ * Inserts 4 bytes aligned on a 4 byte boundary
+ * with any alignment padding initialized to 0.
+ *
+ * @param str the DBusString
+ * @param insert_at where to insert
+ * @param octets 4 bytes to insert
+ * @returns #FALSE if not enough memory.
+ */
+dbus_bool_t
+_dbus_string_insert_4_aligned (DBusString *str,
+ int insert_at,
+ const unsigned char octets[4])
+{
+ DBUS_STRING_PREAMBLE (str);
+
+ if (!align_insert_point_then_open_gap (str, &insert_at, 4, 4))
+ return FALSE;
+
+ memcpy (real->str + insert_at, octets, 4);
+
+ return TRUE;
+}
+
+/**
+ * Inserts 8 bytes aligned on an 8 byte boundary
+ * with any alignment padding initialized to 0.
+ *
+ * @param str the DBusString
+ * @param insert_at where to insert
+ * @param octets 8 bytes to insert
+ * @returns #FALSE if not enough memory.
+ */
+dbus_bool_t
+_dbus_string_insert_8_aligned (DBusString *str,
+ int insert_at,
+ const unsigned char octets[8])
+{
+ DBUS_STRING_PREAMBLE (str);
+
+ if (!align_insert_point_then_open_gap (str, &insert_at, 8, 8))
+ return FALSE;
+
+ _dbus_assert (_DBUS_ALIGN_VALUE (insert_at, 8) == (unsigned) insert_at);
+
+ memcpy (real->str + insert_at, octets, 8);
+
+ return TRUE;
+}
+
+
+/**
+ * Inserts padding at *insert_at such to align it to the given
+ * boundary. Initializes the padding to nul bytes. Sets *insert_at
+ * to the aligned position.
+ *
+ * @param str the DBusString
+ * @param insert_at location to be aligned
+ * @param alignment alignment boundary (1, 2, 4, or 8)
+ * @returns #FALSE if not enough memory.
+ */
+dbus_bool_t
+_dbus_string_insert_alignment (DBusString *str,
+ int *insert_at,
+ int alignment)
+{
+ DBUS_STRING_PREAMBLE (str);
+
+ if (!align_insert_point_then_open_gap (str, insert_at, alignment, 0))
+ return FALSE;
+
+ _dbus_assert (_DBUS_ALIGN_VALUE (*insert_at, alignment) == (unsigned) *insert_at);
+
+ return TRUE;
+}
+
+/**
+ * Appends a printf-style formatted string
+ * to the #DBusString.
+ *
+ * @param str the string
+ * @param format printf format
+ * @param args variable argument list
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_string_append_printf_valist (DBusString *str,
+ const char *format,
+ va_list args)
+{
+ dbus_bool_t ret = FALSE;
+ int len;
+ va_list args_copy;
+
+ DBUS_STRING_PREAMBLE (str);
+
+ va_copy (args_copy, args);
+
+ /* Measure the message length without terminating nul */
+ len = _dbus_printf_string_upper_bound (format, args);
+
+ if (len < 0)
+ goto out;
+
+ if (!_dbus_string_lengthen (str, len))
+ {
+ goto out;
+ }
+
+ vsprintf ((char*) (real->str + (real->len - len)),
+ format, args_copy);
+ ret = TRUE;
+
+out:
+ va_end (args_copy);
+
+ return ret;
+}
+
+/**
+ * Appends a printf-style formatted string
+ * to the #DBusString.
+ *
+ * @param str the string
+ * @param format printf format
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_string_append_printf (DBusString *str,
+ const char *format,
+ ...)
+{
+ va_list args;
+ dbus_bool_t retval;
+
+ va_start (args, format);
+ retval = _dbus_string_append_printf_valist (str, format, args);
+ va_end (args);
+
+ return retval;
+}
+
+/**
+ * Appends block of bytes with the given length to a DBusString.
+ *
+ * @param str the DBusString
+ * @param buffer the bytes to append
+ * @param len the number of bytes to append
+ * @returns #FALSE if not enough memory.
+ */
+dbus_bool_t
+_dbus_string_append_len (DBusString *str,
+ const char *buffer,
+ int len)
+{
+ DBUS_STRING_PREAMBLE (str);
+ _dbus_assert (buffer != NULL);
+ _dbus_assert (len >= 0);
+
+ return append (real, buffer, len);
+}
+
+/**
+ * Appends a single byte to the string, returning #FALSE
+ * if not enough memory.
+ *
+ * @param str the string
+ * @param byte the byte to append
+ * @returns #TRUE on success
+ */
+dbus_bool_t
+_dbus_string_append_byte (DBusString *str,
+ unsigned char byte)
+{
+ DBUS_STRING_PREAMBLE (str);
+
+ if (!set_length (real, real->len + 1))
+ return FALSE;
+
+ real->str[real->len-1] = byte;
+
+ return TRUE;
+}
+
+/**
+ * Append vector with \p strings connected by \p separator
+ *
+ * @param str the string
+ * @param strings vector with char* pointer for merging
+ * @param separator separator to merge the vector
+ * @return #FALSE if not enough memory
+ * @return #TRUE success or empty string vector
+ */
+dbus_bool_t
+_dbus_string_append_strings (DBusString *str, char **strings, char separator)
+{
+ int i;
+
+ if (strings == NULL)
+ return TRUE;
+
+ for (i = 0; strings[i]; i++)
+ {
+ if (i > 0 && !_dbus_string_append_byte (str, (unsigned char) separator))
+ return FALSE;
+
+ if (!_dbus_string_append (str, strings[i]))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+delete (DBusRealString *real,
+ int start,
+ int len)
+{
+ if (len == 0)
+ return;
+
+ memmove (real->str + start, real->str + start + len, real->len - (start + len));
+ real->len -= len;
+ real->str[real->len] = '\0';
+}
+
+/**
+ * Deletes a segment of a DBusString with length len starting at
+ * start. (Hint: to clear an entire string, setting length to 0
+ * with _dbus_string_set_length() is easier.)
+ *
+ * @param str the DBusString
+ * @param start where to start deleting
+ * @param len the number of bytes to delete
+ */
+void
+_dbus_string_delete (DBusString *str,
+ int start,
+ int len)
+{
+ DBUS_STRING_PREAMBLE (str);
+ _dbus_assert (start >= 0);
+ _dbus_assert (len >= 0);
+ _dbus_assert (start <= real->len);
+ _dbus_assert (len <= real->len - start);
+
+ delete (real, start, len);
+}
+
+static dbus_bool_t
+copy (DBusRealString *source,
+ int start,
+ int len,
+ DBusRealString *dest,
+ int insert_at)
+{
+ if (len == 0)
+ return TRUE;
+
+ if (!open_gap (len, dest, insert_at))
+ return FALSE;
+
+ memmove (dest->str + insert_at,
+ source->str + start,
+ len);
+
+ return TRUE;
+}
+
+/**
+ * Checks assertions for two strings we're copying a segment between,
+ * and declares real_source/real_dest variables.
+ *
+ * @param source the source string
+ * @param start the starting offset
+ * @param dest the dest string
+ * @param insert_at where the copied segment is inserted
+ */
+#define DBUS_STRING_COPY_PREAMBLE(source, start, dest, insert_at) \
+ DBusRealString *real_source = (DBusRealString*) source; \
+ DBusRealString *real_dest = (DBusRealString*) dest; \
+ _dbus_assert ((source) != (dest)); \
+ DBUS_GENERIC_STRING_PREAMBLE (real_source); \
+ DBUS_GENERIC_STRING_PREAMBLE (real_dest); \
+ _dbus_assert (!real_dest->constant); \
+ _dbus_assert (!real_dest->locked); \
+ _dbus_assert ((start) >= 0); \
+ _dbus_assert ((start) <= real_source->len); \
+ _dbus_assert ((insert_at) >= 0); \
+ _dbus_assert ((insert_at) <= real_dest->len)
+
+/**
+ * Moves the end of one string into another string. Both strings
+ * must be initialized, valid strings.
+ *
+ * @param source the source string
+ * @param start where to chop off the source string
+ * @param dest the destination string
+ * @param insert_at where to move the chopped-off part of source string
+ * @returns #FALSE if not enough memory
+ */
+dbus_bool_t
+_dbus_string_move (DBusString *source,
+ int start,
+ DBusString *dest,
+ int insert_at)
+{
+ DBusRealString *real_source = (DBusRealString*) source;
+ _dbus_assert (start <= real_source->len);
+
+ return _dbus_string_move_len (source, start,
+ real_source->len - start,
+ dest, insert_at);
+}
+
+/**
+ * Like _dbus_string_move(), but does not delete the section
+ * of the source string that's copied to the dest string.
+ *
+ * @param source the source string
+ * @param start where to start copying the source string
+ * @param dest the destination string
+ * @param insert_at where to place the copied part of source string
+ * @returns #FALSE if not enough memory
+ */
+dbus_bool_t
+_dbus_string_copy (const DBusString *source,
+ int start,
+ DBusString *dest,
+ int insert_at)
+{
+ DBUS_STRING_COPY_PREAMBLE (source, start, dest, insert_at);
+
+ return copy (real_source, start,
+ real_source->len - start,
+ real_dest,
+ insert_at);
+}
+
+/**
+ * Like _dbus_string_move(), but can move a segment from
+ * the middle of the source string.
+ *
+ * @param source the source string
+ * @param start first byte of source string to move
+ * @param len length of segment to move
+ * @param dest the destination string
+ * @param insert_at where to move the bytes from the source string
+ * @returns #FALSE if not enough memory
+ */
+dbus_bool_t
+_dbus_string_move_len (DBusString *source,
+ int start,
+ int len,
+ DBusString *dest,
+ int insert_at)
+
+{
+ DBUS_STRING_COPY_PREAMBLE (source, start, dest, insert_at);
+ _dbus_assert (len >= 0);
+ _dbus_assert ((start + len) <= real_source->len);
+
+
+ if (len == 0)
+ {
+ return TRUE;
+ }
+ else if (start == 0 &&
+ len == real_source->len &&
+ real_dest->len == 0)
+ {
+ /* Short-circuit moving an entire existing string to an empty string
+ * by just swapping the buffers.
+ */
+ /* we assume ->constant doesn't matter as you can't have
+ * a constant string involved in a move.
+ */
+#define ASSIGN_DATA(a, b) do { \
+ (a)->str = (b)->str; \
+ (a)->len = (b)->len; \
+ (a)->allocated = (b)->allocated; \
+ (a)->align_offset = (b)->align_offset; \
+ } while (0)
+
+ DBusRealString tmp;
+
+ ASSIGN_DATA (&tmp, real_source);
+ ASSIGN_DATA (real_source, real_dest);
+ ASSIGN_DATA (real_dest, &tmp);
+
+ return TRUE;
+ }
+ else
+ {
+ if (!copy (real_source, start, len,
+ real_dest,
+ insert_at))
+ return FALSE;
+
+ delete (real_source, start,
+ len);
+
+ return TRUE;
+ }
+}
+
+/**
+ * Like _dbus_string_copy(), but can copy a segment from the middle of
+ * the source string.
+ *
+ * @param source the source string
+ * @param start where to start copying the source string
+ * @param len length of segment to copy
+ * @param dest the destination string
+ * @param insert_at where to place the copied segment of source string
+ * @returns #FALSE if not enough memory
+ */
+dbus_bool_t
+_dbus_string_copy_len (const DBusString *source,
+ int start,
+ int len,
+ DBusString *dest,
+ int insert_at)
+{
+ DBUS_STRING_COPY_PREAMBLE (source, start, dest, insert_at);
+ _dbus_assert (len >= 0);
+ _dbus_assert (start <= real_source->len);
+ _dbus_assert (len <= real_source->len - start);
+
+ return copy (real_source, start, len,
+ real_dest,
+ insert_at);
+}
+
+/**
+ * Replaces a segment of dest string with a segment of source string.
+ *
+ * @param source the source string
+ * @param start where to start copying the source string
+ * @param len length of segment to copy
+ * @param dest the destination string
+ * @param replace_at start of segment of dest string to replace
+ * @param replace_len length of segment of dest string to replace
+ * @returns #FALSE if not enough memory
+ *
+ */
+dbus_bool_t
+_dbus_string_replace_len (const DBusString *source,
+ int start,
+ int len,
+ DBusString *dest,
+ int replace_at,
+ int replace_len)
+{
+ DBUS_STRING_COPY_PREAMBLE (source, start, dest, replace_at);
+ _dbus_assert (len >= 0);
+ _dbus_assert (start <= real_source->len);
+ _dbus_assert (len <= real_source->len - start);
+ _dbus_assert (replace_at >= 0);
+ _dbus_assert (replace_at <= real_dest->len);
+ _dbus_assert (replace_len <= real_dest->len - replace_at);
+
+ if (len == replace_len)
+ {
+ memmove (real_dest->str + replace_at,
+ real_source->str + start, len);
+ }
+ else if (len < replace_len)
+ {
+ memmove (real_dest->str + replace_at,
+ real_source->str + start, len);
+ delete (real_dest, replace_at + len,
+ replace_len - len);
+ }
+ else
+ {
+ int diff;
+
+ _dbus_assert (len > replace_len);
+
+ diff = len - replace_len;
+
+ /* First of all we check if destination string can be enlarged as
+ * required, then we overwrite previous bytes
+ */
+
+ if (!copy (real_source, start + replace_len, diff,
+ real_dest, replace_at + replace_len))
+ return FALSE;
+
+ memmove (real_dest->str + replace_at,
+ real_source->str + start, replace_len);
+ }
+
+ return TRUE;
+}
+
+/**
+ * Looks for the first occurance of a byte, deletes that byte,
+ * and moves everything after the byte to the beginning of a
+ * separate string. Both strings must be initialized, valid
+ * strings.
+ *
+ * @param source the source string
+ * @param byte the byte to remove and split the string at
+ * @param tail the split off string
+ * @returns #FALSE if not enough memory or if byte could not be found
+ *
+ */
+dbus_bool_t
+_dbus_string_split_on_byte (DBusString *source,
+ unsigned char byte,
+ DBusString *tail)
+{
+ int byte_position;
+ char byte_string[2] = "";
+ int head_length;
+ int tail_length;
+
+ byte_string[0] = (char) byte;
+
+ if (!_dbus_string_find (source, 0, byte_string, &byte_position))
+ return FALSE;
+
+ head_length = byte_position;
+ tail_length = _dbus_string_get_length (source) - head_length - 1;
+
+ if (!_dbus_string_move_len (source, byte_position + 1, tail_length,
+ tail, 0))
+ return FALSE;
+
+ /* remove the trailing delimiter byte from the head now.
+ */
+ if (!_dbus_string_set_length (source, head_length))
+ return FALSE;
+
+ return TRUE;
+}
+
+/* Unicode macros and utf8_validate() from GLib Owen Taylor, Havoc
+ * Pennington, and Tom Tromey are the authors and authorized relicense.
+ */
+
+/** computes length and mask of a unicode character
+ * @param Char the char
+ * @param Mask the mask variable to assign to
+ * @param Len the length variable to assign to
+ */
+#define UTF8_COMPUTE(Char, Mask, Len) \
+ if (Char < 128) \
+ { \
+ Len = 1; \
+ Mask = 0x7f; \
+ } \
+ else if ((Char & 0xe0) == 0xc0) \
+ { \
+ Len = 2; \
+ Mask = 0x1f; \
+ } \
+ else if ((Char & 0xf0) == 0xe0) \
+ { \
+ Len = 3; \
+ Mask = 0x0f; \
+ } \
+ else if ((Char & 0xf8) == 0xf0) \
+ { \
+ Len = 4; \
+ Mask = 0x07; \
+ } \
+ else if ((Char & 0xfc) == 0xf8) \
+ { \
+ Len = 5; \
+ Mask = 0x03; \
+ } \
+ else if ((Char & 0xfe) == 0xfc) \
+ { \
+ Len = 6; \
+ Mask = 0x01; \
+ } \
+ else \
+ { \
+ Len = 0; \
+ Mask = 0; \
+ }
+
+/**
+ * computes length of a unicode character in UTF-8
+ * @param Char the char
+ */
+#define UTF8_LENGTH(Char) \
+ ((Char) < 0x80 ? 1 : \
+ ((Char) < 0x800 ? 2 : \
+ ((Char) < 0x10000 ? 3 : \
+ ((Char) < 0x200000 ? 4 : \
+ ((Char) < 0x4000000 ? 5 : 6)))))
+
+/**
+ * Gets a UTF-8 value.
+ *
+ * @param Result variable for extracted unicode char.
+ * @param Chars the bytes to decode
+ * @param Count counter variable
+ * @param Mask mask for this char
+ * @param Len length for this char in bytes
+ */
+#define UTF8_GET(Result, Chars, Count, Mask, Len) \
+ (Result) = (Chars)[0] & (Mask); \
+ for ((Count) = 1; (Count) < (Len); ++(Count)) \
+ { \
+ if (((Chars)[(Count)] & 0xc0) != 0x80) \
+ { \
+ (Result) = -1; \
+ break; \
+ } \
+ (Result) <<= 6; \
+ (Result) |= ((Chars)[(Count)] & 0x3f); \
+ }
+
+/**
+ * Check whether a Unicode (5.2) char is in a valid range.
+ *
+ * The first check comes from the Unicode guarantee to never encode
+ * a point above 0x0010ffff, since UTF-16 couldn't represent it.
+ *
+ * The second check covers surrogate pairs (category Cs).
+ *
+ * @param Char the character
+ */
+#define UNICODE_VALID(Char) \
+ ((Char) < 0x110000 && \
+ (((Char) & 0xFFFFF800) != 0xD800))
+
+/**
+ * Finds the given substring in the string,
+ * returning #TRUE and filling in the byte index
+ * where the substring was found, if it was found.
+ * Returns #FALSE if the substring wasn't found.
+ * Sets *start to the length of the string if the substring
+ * is not found.
+ *
+ * @param str the string
+ * @param start where to start looking
+ * @param substr the substring
+ * @param found return location for where it was found, or #NULL
+ * @returns #TRUE if found
+ */
+dbus_bool_t
+_dbus_string_find (const DBusString *str,
+ int start,
+ const char *substr,
+ int *found)
+{
+ return _dbus_string_find_to (str, start,
+ ((const DBusRealString*)str)->len,
+ substr, found);
+}
+
+/**
+ * Finds end of line ("\r\n" or "\n") in the string,
+ * returning #TRUE and filling in the byte index
+ * where the eol string was found, if it was found.
+ * Returns #FALSE if eol wasn't found.
+ *
+ * @param str the string
+ * @param start where to start looking
+ * @param found return location for where eol was found or string length otherwise
+ * @param found_len return length of found eol string or zero otherwise
+ * @returns #TRUE if found
+ */
+dbus_bool_t
+_dbus_string_find_eol (const DBusString *str,
+ int start,
+ int *found,
+ int *found_len)
+{
+ int i;
+
+ DBUS_CONST_STRING_PREAMBLE (str);
+ _dbus_assert (start <= real->len);
+ _dbus_assert (start >= 0);
+
+ i = start;
+ while (i < real->len)
+ {
+ if (real->str[i] == '\r')
+ {
+ if ((i+1) < real->len && real->str[i+1] == '\n') /* "\r\n" */
+ {
+ if (found)
+ *found = i;
+ if (found_len)
+ *found_len = 2;
+ return TRUE;
+ }
+ else /* only "\r" */
+ {
+ if (found)
+ *found = i;
+ if (found_len)
+ *found_len = 1;
+ return TRUE;
+ }
+ }
+ else if (real->str[i] == '\n') /* only "\n" */
+ {
+ if (found)
+ *found = i;
+ if (found_len)
+ *found_len = 1;
+ return TRUE;
+ }
+ ++i;
+ }
+
+ if (found)
+ *found = real->len;
+
+ if (found_len)
+ *found_len = 0;
+
+ return FALSE;
+}
+
+/**
+ * Finds the given substring in the string,
+ * up to a certain position,
+ * returning #TRUE and filling in the byte index
+ * where the substring was found, if it was found.
+ * Returns #FALSE if the substring wasn't found.
+ * Sets *start to the length of the string if the substring
+ * is not found.
+ *
+ * @param str the string
+ * @param start where to start looking
+ * @param end where to stop looking
+ * @param substr the substring
+ * @param found return location for where it was found, or #NULL
+ * @returns #TRUE if found
+ */
+dbus_bool_t
+_dbus_string_find_to (const DBusString *str,
+ int start,
+ int end,
+ const char *substr,
+ int *found)
+{
+ int i;
+ DBUS_CONST_STRING_PREAMBLE (str);
+ _dbus_assert (substr != NULL);
+ _dbus_assert (start <= real->len);
+ _dbus_assert (start >= 0);
+ _dbus_assert (substr != NULL);
+ _dbus_assert (end <= real->len);
+ _dbus_assert (start <= end);
+
+ /* we always "find" an empty string */
+ if (*substr == '\0')
+ {
+ if (found)
+ *found = start;
+ return TRUE;
+ }
+
+ i = start;
+ while (i < end)
+ {
+ if (real->str[i] == substr[0])
+ {
+ int j = i + 1;
+
+ while (j < end)
+ {
+ if (substr[j - i] == '\0')
+ break;
+ else if (real->str[j] != substr[j - i])
+ break;
+
+ ++j;
+ }
+
+ if (substr[j - i] == '\0')
+ {
+ if (found)
+ *found = i;
+ return TRUE;
+ }
+ }
+
+ ++i;
+ }
+
+ if (found)
+ *found = end;
+
+ return FALSE;
+}
+
+/**
+ * Finds a blank (space or tab) in the string. Returns #TRUE
+ * if found, #FALSE otherwise. If a blank is not found sets
+ * *found to the length of the string.
+ *
+ * @param str the string
+ * @param start byte index to start looking
+ * @param found place to store the location of the first blank
+ * @returns #TRUE if a blank was found
+ */
+dbus_bool_t
+_dbus_string_find_blank (const DBusString *str,
+ int start,
+ int *found)
+{
+ int i;
+ DBUS_CONST_STRING_PREAMBLE (str);
+ _dbus_assert (start <= real->len);
+ _dbus_assert (start >= 0);
+
+ i = start;
+ while (i < real->len)
+ {
+ if (real->str[i] == ' ' ||
+ real->str[i] == '\t')
+ {
+ if (found)
+ *found = i;
+ return TRUE;
+ }
+
+ ++i;
+ }
+
+ if (found)
+ *found = real->len;
+
+ return FALSE;
+}
+
+/**
+ * Skips blanks from start, storing the first non-blank in *end
+ * (blank is space or tab).
+ *
+ * @param str the string
+ * @param start where to start
+ * @param end where to store the first non-blank byte index
+ */
+void
+_dbus_string_skip_blank (const DBusString *str,
+ int start,
+ int *end)
+{
+ int i;
+ DBUS_CONST_STRING_PREAMBLE (str);
+ _dbus_assert (start <= real->len);
+ _dbus_assert (start >= 0);
+
+ i = start;
+ while (i < real->len)
+ {
+ if (!DBUS_IS_ASCII_BLANK (real->str[i]))
+ break;
+
+ ++i;
+ }
+
+ _dbus_assert (i == real->len || !DBUS_IS_ASCII_BLANK (real->str[i]));
+
+ if (end)
+ *end = i;
+}
+
+
+/**
+ * Skips whitespace from start, storing the first non-whitespace in *end.
+ * (whitespace is space, tab, newline, CR).
+ *
+ * @param str the string
+ * @param start where to start
+ * @param end where to store the first non-whitespace byte index
+ */
+void
+_dbus_string_skip_white (const DBusString *str,
+ int start,
+ int *end)
+{
+ int i;
+ DBUS_CONST_STRING_PREAMBLE (str);
+ _dbus_assert (start <= real->len);
+ _dbus_assert (start >= 0);
+
+ i = start;
+ while (i < real->len)
+ {
+ if (!DBUS_IS_ASCII_WHITE (real->str[i]))
+ break;
+
+ ++i;
+ }
+
+ _dbus_assert (i == real->len || !(DBUS_IS_ASCII_WHITE (real->str[i])));
+
+ if (end)
+ *end = i;
+}
+
+/**
+ * Skips whitespace from end, storing the start index of the trailing
+ * whitespace in *start. (whitespace is space, tab, newline, CR).
+ *
+ * @param str the string
+ * @param end where to start scanning backward
+ * @param start where to store the start of whitespace chars
+ */
+void
+_dbus_string_skip_white_reverse (const DBusString *str,
+ int end,
+ int *start)
+{
+ int i;
+ DBUS_CONST_STRING_PREAMBLE (str);
+ _dbus_assert (end <= real->len);
+ _dbus_assert (end >= 0);
+
+ i = end;
+ while (i > 0)
+ {
+ if (!DBUS_IS_ASCII_WHITE (real->str[i-1]))
+ break;
+ --i;
+ }
+
+ _dbus_assert (i >= 0 && (i == 0 || !(DBUS_IS_ASCII_WHITE (real->str[i-1]))));
+
+ if (start)
+ *start = i;
+}
+
+/**
+ * Assigns a newline-terminated or \\r\\n-terminated line from the front
+ * of the string to the given dest string. The dest string's previous
+ * contents are deleted. If the source string contains no newline,
+ * moves the entire source string to the dest string.
+ *
+ * @todo owen correctly notes that this is a stupid function (it was
+ * written purely for test code,
+ * e.g. dbus-message-builder.c). Probably should be enforced as test
+ * code only with ifdef DBUS_ENABLE_EMBEDDED_TESTS
+ *
+ * @param source the source string
+ * @param dest the destination string (contents are replaced)
+ * @returns #FALSE if no memory, or source has length 0
+ */
+dbus_bool_t
+_dbus_string_pop_line (DBusString *source,
+ DBusString *dest)
+{
+ int eol, eol_len;
+
+ _dbus_string_set_length (dest, 0);
+
+ eol = 0;
+ eol_len = 0;
+ if (!_dbus_string_find_eol (source, 0, &eol, &eol_len))
+ {
+ _dbus_assert (eol == _dbus_string_get_length (source));
+ if (eol == 0)
+ {
+ /* If there's no newline and source has zero length, we're done */
+ return FALSE;
+ }
+ /* otherwise, the last line of the file has no eol characters */
+ }
+
+ /* remember eol can be 0 if it's an empty line, but eol_len should not be zero also
+ * since find_eol returned TRUE
+ */
+
+ if (!_dbus_string_move_len (source, 0, eol + eol_len, dest, 0))
+ return FALSE;
+
+ /* remove line ending */
+ if (!_dbus_string_set_length (dest, eol))
+ {
+ _dbus_assert_not_reached ("out of memory when shortening a string");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
+/**
+ * Deletes up to and including the first blank space
+ * in the string.
+ *
+ * @param str the string
+ */
+void
+_dbus_string_delete_first_word (DBusString *str)
+{
+ int i;
+
+ if (_dbus_string_find_blank (str, 0, &i))
+ _dbus_string_skip_blank (str, i, &i);
+
+ _dbus_string_delete (str, 0, i);
+}
+#endif
+
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
+/**
+ * Deletes any leading blanks in the string
+ *
+ * @param str the string
+ */
+void
+_dbus_string_delete_leading_blanks (DBusString *str)
+{
+ int i;
+
+ _dbus_string_skip_blank (str, 0, &i);
+
+ if (i > 0)
+ _dbus_string_delete (str, 0, i);
+}
+#endif
+
+/**
+ * Deletes leading and trailing whitespace
+ *
+ * @param str the string
+ */
+void
+_dbus_string_chop_white(DBusString *str)
+{
+ int i;
+
+ _dbus_string_skip_white (str, 0, &i);
+
+ if (i > 0)
+ _dbus_string_delete (str, 0, i);
+
+ _dbus_string_skip_white_reverse (str, _dbus_string_get_length (str), &i);
+
+ _dbus_string_set_length (str, i);
+}
+
+/**
+ * Tests two DBusString for equality.
+ *
+ * @todo memcmp is probably faster
+ *
+ * @param a first string
+ * @param b second string
+ * @returns #TRUE if equal
+ */
+dbus_bool_t
+_dbus_string_equal (const DBusString *a,
+ const DBusString *b)
+{
+ const unsigned char *ap;
+ const unsigned char *bp;
+ const unsigned char *a_end;
+ const DBusRealString *real_a = (const DBusRealString*) a;
+ const DBusRealString *real_b = (const DBusRealString*) b;
+ DBUS_GENERIC_STRING_PREAMBLE (real_a);
+ DBUS_GENERIC_STRING_PREAMBLE (real_b);
+
+ if (real_a->len != real_b->len)
+ return FALSE;
+
+ ap = real_a->str;
+ bp = real_b->str;
+ a_end = real_a->str + real_a->len;
+ while (ap != a_end)
+ {
+ if (*ap != *bp)
+ return FALSE;
+
+ ++ap;
+ ++bp;
+ }
+
+ return TRUE;
+}
+
+/**
+ * Tests two DBusString for equality up to the given length.
+ * The strings may be shorter than the given length.
+ *
+ * @todo write a unit test
+ *
+ * @todo memcmp is probably faster
+ *
+ * @param a first string
+ * @param b second string
+ * @param len the maximum length to look at
+ * @returns #TRUE if equal for the given number of bytes
+ */
+dbus_bool_t
+_dbus_string_equal_len (const DBusString *a,
+ const DBusString *b,
+ int len)
+{
+ const unsigned char *ap;
+ const unsigned char *bp;
+ const unsigned char *a_end;
+ const DBusRealString *real_a = (const DBusRealString*) a;
+ const DBusRealString *real_b = (const DBusRealString*) b;
+ DBUS_GENERIC_STRING_PREAMBLE (real_a);
+ DBUS_GENERIC_STRING_PREAMBLE (real_b);
+
+ if (real_a->len != real_b->len &&
+ (real_a->len < len || real_b->len < len))
+ return FALSE;
+
+ ap = real_a->str;
+ bp = real_b->str;
+ a_end = real_a->str + MIN (real_a->len, len);
+ while (ap != a_end)
+ {
+ if (*ap != *bp)
+ return FALSE;
+
+ ++ap;
+ ++bp;
+ }
+
+ return TRUE;
+}
+
+/**
+ * Tests two sub-parts of two DBusString for equality. The specified
+ * range of the first string must exist; the specified start position
+ * of the second string must exist.
+ *
+ * @todo write a unit test
+ *
+ * @todo memcmp is probably faster
+ *
+ * @param a first string
+ * @param a_start where to start substring in first string
+ * @param a_len length of substring in first string
+ * @param b second string
+ * @param b_start where to start substring in second string
+ * @returns #TRUE if the two substrings are equal
+ */
+dbus_bool_t
+_dbus_string_equal_substring (const DBusString *a,
+ int a_start,
+ int a_len,
+ const DBusString *b,
+ int b_start)
+{
+ const unsigned char *ap;
+ const unsigned char *bp;
+ const unsigned char *a_end;
+ const DBusRealString *real_a = (const DBusRealString*) a;
+ const DBusRealString *real_b = (const DBusRealString*) b;
+ DBUS_GENERIC_STRING_PREAMBLE (real_a);
+ DBUS_GENERIC_STRING_PREAMBLE (real_b);
+ _dbus_assert (a_start >= 0);
+ _dbus_assert (a_len >= 0);
+ _dbus_assert (a_start <= real_a->len);
+ _dbus_assert (a_len <= real_a->len - a_start);
+ _dbus_assert (b_start >= 0);
+ _dbus_assert (b_start <= real_b->len);
+
+ if (a_len > real_b->len - b_start)
+ return FALSE;
+
+ ap = real_a->str + a_start;
+ bp = real_b->str + b_start;
+ a_end = ap + a_len;
+ while (ap != a_end)
+ {
+ if (*ap != *bp)
+ return FALSE;
+
+ ++ap;
+ ++bp;
+ }
+
+ _dbus_assert (bp <= (real_b->str + real_b->len));
+
+ return TRUE;
+}
+
+/**
+ * Checks whether a string is equal to a C string.
+ *
+ * @param a the string
+ * @param c_str the C string
+ * @returns #TRUE if equal
+ */
+dbus_bool_t
+_dbus_string_equal_c_str (const DBusString *a,
+ const char *c_str)
+{
+ const unsigned char *ap;
+ const unsigned char *bp;
+ const unsigned char *a_end;
+ const DBusRealString *real_a = (const DBusRealString*) a;
+ DBUS_GENERIC_STRING_PREAMBLE (real_a);
+ _dbus_assert (c_str != NULL);
+
+ ap = real_a->str;
+ bp = (const unsigned char*) c_str;
+ a_end = real_a->str + real_a->len;
+ while (ap != a_end && *bp)
+ {
+ if (*ap != *bp)
+ return FALSE;
+
+ ++ap;
+ ++bp;
+ }
+
+ if (ap != a_end || *bp)
+ return FALSE;
+
+ return TRUE;
+}
+
+/**
+ * Checks whether a string starts with the given C string.
+ *
+ * @param a the string
+ * @param c_str the C string
+ * @returns #TRUE if string starts with it
+ */
+dbus_bool_t
+_dbus_string_starts_with_c_str (const DBusString *a,
+ const char *c_str)
+{
+ const unsigned char *ap;
+ const unsigned char *bp;
+ const unsigned char *a_end;
+ const DBusRealString *real_a = (const DBusRealString*) a;
+ DBUS_GENERIC_STRING_PREAMBLE (real_a);
+ _dbus_assert (c_str != NULL);
+
+ ap = real_a->str;
+ bp = (const unsigned char*) c_str;
+ a_end = real_a->str + real_a->len;
+ while (ap != a_end && *bp)
+ {
+ if (*ap != *bp)
+ return FALSE;
+
+ ++ap;
+ ++bp;
+ }
+
+ if (*bp == '\0')
+ return TRUE;
+ else
+ return FALSE;
+}
+
+/**
+ * Checks whether a string starts with the given C string, after which it ends or is separated from
+ * the rest by a given separator character.
+ *
+ * @param a the string
+ * @param c_str the C string
+ * @param word_separator the separator
+ * @returns #TRUE if string starts with it
+ */
+dbus_bool_t
+_dbus_string_starts_with_words_c_str (const DBusString *a,
+ const char *c_str,
+ char word_separator)
+{
+ char next_char;
+ const char *data;
+ _dbus_assert (c_str != NULL);
+
+ if (!_dbus_string_starts_with_c_str (a, c_str))
+ return FALSE;
+
+ data = _dbus_string_get_const_data (a);
+ next_char = data[strlen (c_str)];
+ return next_char == '\0' || next_char == word_separator;
+}
+
+/**
+ * Appends a two-character hex digit to a string, where the hex digit
+ * has the value of the given byte.
+ *
+ * @param str the string
+ * @param byte the byte
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_string_append_byte_as_hex (DBusString *str,
+ unsigned char byte)
+{
+ const char hexdigits[16] = {
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ 'a', 'b', 'c', 'd', 'e', 'f'
+ };
+
+ if (!_dbus_string_append_byte (str,
+ hexdigits[(byte >> 4)]))
+ return FALSE;
+
+ if (!_dbus_string_append_byte (str,
+ hexdigits[(byte & 0x0f)]))
+ {
+ _dbus_string_set_length (str,
+ _dbus_string_get_length (str) - 1);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/* Currently only used when embedded tests are enabled */
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
+/**
+ * Appends \p size bytes from the buffer \p buf as hex digits to the string \p str
+ *
+ * If \p size is nonzero, then \p buf must be non-NULL.
+ *
+ * @param str the string
+ * @param buf the buffer to add bytes from
+ * @param size the number of bytes to add
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_string_append_buffer_as_hex (DBusString *str,
+ void *buf,
+ int size)
+{
+ unsigned char *p;
+ int i;
+
+ _dbus_assert (size >= 0);
+ _dbus_assert (size == 0 || buf != NULL);
+
+ p = (unsigned char *) buf;
+
+ for (i = 0; i < size; i++)
+ {
+ if (!_dbus_string_append_byte_as_hex (str, p[i]))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+#endif
+
+/**
+ * Encodes a string in hex, the way MD5 and SHA-1 are usually
+ * encoded. (Each byte is two hex digits.)
+ *
+ * @param source the string to encode
+ * @param start byte index to start encoding
+ * @param dest string where encoded data should be placed
+ * @param insert_at where to place encoded data
+ * @returns #TRUE if encoding was successful, #FALSE if no memory etc.
+ */
+dbus_bool_t
+_dbus_string_hex_encode (const DBusString *source,
+ int start,
+ DBusString *dest,
+ int insert_at)
+{
+ DBusString result;
+ const unsigned char *p;
+ const unsigned char *end;
+ dbus_bool_t retval;
+
+ _dbus_assert (start <= _dbus_string_get_length (source));
+
+ if (!_dbus_string_init (&result))
+ return FALSE;
+
+ retval = FALSE;
+
+ p = (const unsigned char*) _dbus_string_get_const_data (source);
+ end = p + _dbus_string_get_length (source);
+ p += start;
+
+ while (p != end)
+ {
+ if (!_dbus_string_append_byte_as_hex (&result, *p))
+ goto out;
+
+ ++p;
+ }
+
+ if (!_dbus_string_move (&result, 0, dest, insert_at))
+ goto out;
+
+ retval = TRUE;
+
+ out:
+ _dbus_string_free (&result);
+ return retval;
+}
+
+/**
+ * Decodes a string from hex encoding.
+ *
+ * @param source the string to decode
+ * @param start byte index to start decode
+ * @param end_return return location of the end of the hex data, or #NULL
+ * @param dest string where decoded data should be placed
+ * @param insert_at where to place decoded data
+ * @returns #TRUE if decoding was successful, #FALSE if no memory.
+ */
+dbus_bool_t
+_dbus_string_hex_decode (const DBusString *source,
+ int start,
+ int *end_return,
+ DBusString *dest,
+ int insert_at)
+{
+ DBusString result;
+ const unsigned char *p;
+ const unsigned char *end;
+ dbus_bool_t retval;
+ dbus_bool_t high_bits;
+
+ _dbus_assert (start <= _dbus_string_get_length (source));
+
+ if (!_dbus_string_init (&result))
+ return FALSE;
+
+ retval = FALSE;
+
+ high_bits = TRUE;
+ p = (const unsigned char*) _dbus_string_get_const_data (source);
+ end = p + _dbus_string_get_length (source);
+ p += start;
+
+ while (p != end)
+ {
+ unsigned int val;
+
+ switch (*p)
+ {
+ case '0':
+ val = 0;
+ break;
+ case '1':
+ val = 1;
+ break;
+ case '2':
+ val = 2;
+ break;
+ case '3':
+ val = 3;
+ break;
+ case '4':
+ val = 4;
+ break;
+ case '5':
+ val = 5;
+ break;
+ case '6':
+ val = 6;
+ break;
+ case '7':
+ val = 7;
+ break;
+ case '8':
+ val = 8;
+ break;
+ case '9':
+ val = 9;
+ break;
+ case 'a':
+ case 'A':
+ val = 10;
+ break;
+ case 'b':
+ case 'B':
+ val = 11;
+ break;
+ case 'c':
+ case 'C':
+ val = 12;
+ break;
+ case 'd':
+ case 'D':
+ val = 13;
+ break;
+ case 'e':
+ case 'E':
+ val = 14;
+ break;
+ case 'f':
+ case 'F':
+ val = 15;
+ break;
+ default:
+ goto done;
+ }
+
+ if (high_bits)
+ {
+ if (!_dbus_string_append_byte (&result,
+ val << 4))
+ goto out;
+ }
+ else
+ {
+ int len;
+ unsigned char b;
+
+ len = _dbus_string_get_length (&result);
+
+ b = _dbus_string_get_byte (&result, len - 1);
+
+ b |= val;
+
+ _dbus_string_set_byte (&result, len - 1, b);
+ }
+
+ high_bits = !high_bits;
+
+ ++p;
+ }
+
+ done:
+ if (!_dbus_string_move (&result, 0, dest, insert_at))
+ goto out;
+
+ if (end_return)
+ *end_return = p - (const unsigned char*) _dbus_string_get_const_data (source);
+
+ retval = TRUE;
+
+ out:
+ _dbus_string_free (&result);
+ return retval;
+}
+
+/**
+ * Checks that the given range of the string is valid ASCII with no
+ * nul bytes. If the given range is not entirely contained in the
+ * string, returns #FALSE.
+ *
+ * @todo this is inconsistent with most of DBusString in that
+ * it allows a start,len range that extends past the string end.
+ *
+ * @param str the string
+ * @param start first byte index to check
+ * @param len number of bytes to check
+ * @returns #TRUE if the byte range exists and is all valid ASCII
+ */
+dbus_bool_t
+_dbus_string_validate_ascii (const DBusString *str,
+ int start,
+ int len)
+{
+ const unsigned char *s;
+ const unsigned char *end;
+ DBUS_CONST_STRING_PREAMBLE (str);
+ _dbus_assert (start >= 0);
+ _dbus_assert (start <= real->len);
+ _dbus_assert (len >= 0);
+
+ if (len > real->len - start)
+ return FALSE;
+
+ s = real->str + start;
+ end = s + len;
+ while (s != end)
+ {
+ if (_DBUS_UNLIKELY (!_DBUS_ISASCII (*s)))
+ return FALSE;
+
+ ++s;
+ }
+
+ return TRUE;
+}
+
+/**
+ * Converts the given range of the string to lower case.
+ *
+ * @param str the string
+ * @param start first byte index to convert
+ * @param len number of bytes to convert
+ */
+void
+_dbus_string_tolower_ascii (const DBusString *str,
+ int start,
+ int len)
+{
+ unsigned char *s;
+ unsigned char *end;
+ DBUS_STRING_PREAMBLE (str);
+ _dbus_assert (start >= 0);
+ _dbus_assert (start <= real->len);
+ _dbus_assert (len >= 0);
+ _dbus_assert (len <= real->len - start);
+
+ s = real->str + start;
+ end = s + len;
+
+ while (s != end)
+ {
+ if (*s >= 'A' && *s <= 'Z')
+ *s += 'a' - 'A';
+ ++s;
+ }
+}
+
+/**
+ * Converts the given range of the string to upper case.
+ *
+ * @param str the string
+ * @param start first byte index to convert
+ * @param len number of bytes to convert
+ */
+void
+_dbus_string_toupper_ascii (const DBusString *str,
+ int start,
+ int len)
+{
+ unsigned char *s;
+ unsigned char *end;
+ DBUS_STRING_PREAMBLE (str);
+ _dbus_assert (start >= 0);
+ _dbus_assert (start <= real->len);
+ _dbus_assert (len >= 0);
+ _dbus_assert (len <= real->len - start);
+
+ s = real->str + start;
+ end = s + len;
+
+ while (s != end)
+ {
+ if (*s >= 'a' && *s <= 'z')
+ *s += 'A' - 'a';
+ ++s;
+ }
+}
+
+/**
+ * Checks that the given range of the string is valid UTF-8. If the
+ * given range is not entirely contained in the string, returns
+ * #FALSE. If the string contains any nul bytes in the given range,
+ * returns #FALSE. If the start and start+len are not on character
+ * boundaries, returns #FALSE.
+ *
+ * @todo this is inconsistent with most of DBusString in that
+ * it allows a start,len range that extends past the string end.
+ *
+ * @param str the string
+ * @param start first byte index to check
+ * @param len number of bytes to check
+ * @returns #TRUE if the byte range exists and is all valid UTF-8
+ */
+dbus_bool_t
+_dbus_string_validate_utf8 (const DBusString *str,
+ int start,
+ int len)
+{
+ const unsigned char *p;
+ const unsigned char *end;
+ DBUS_CONST_STRING_PREAMBLE (str);
+ _dbus_assert (start >= 0);
+ _dbus_assert (start <= real->len);
+ _dbus_assert (len >= 0);
+
+ /* we are doing _DBUS_UNLIKELY() here which might be
+ * dubious in a generic library like GLib, but in D-Bus
+ * we know we're validating messages and that it would
+ * only be evil/broken apps that would have invalid
+ * UTF-8. Also, this function seems to be a performance
+ * bottleneck in profiles.
+ */
+
+ if (_DBUS_UNLIKELY (len > real->len - start))
+ return FALSE;
+
+ p = real->str + start;
+ end = p + len;
+
+ while (p < end)
+ {
+ int i, mask, char_len;
+ dbus_unichar_t result;
+
+ /* nul bytes considered invalid */
+ if (*p == '\0')
+ break;
+
+ /* Special-case ASCII; this makes us go a lot faster in
+ * D-Bus profiles where we are typically validating
+ * function names and such. We have to know that
+ * all following checks will pass for ASCII though,
+ * comments follow ...
+ */
+ if (*p < 128)
+ {
+ ++p;
+ continue;
+ }
+
+ UTF8_COMPUTE (*p, mask, char_len);
+
+ if (_DBUS_UNLIKELY (char_len == 0)) /* ASCII: char_len == 1 */
+ break;
+
+ /* check that the expected number of bytes exists in the remaining length */
+ if (_DBUS_UNLIKELY ((end - p) < char_len)) /* ASCII: p < end and char_len == 1 */
+ break;
+
+ UTF8_GET (result, p, i, mask, char_len);
+
+ /* Check for overlong UTF-8 */
+ if (_DBUS_UNLIKELY (UTF8_LENGTH (result) != char_len)) /* ASCII: UTF8_LENGTH == 1 */
+ break;
+#if 0
+ /* The UNICODE_VALID check below will catch this */
+ if (_DBUS_UNLIKELY (result == (dbus_unichar_t)-1)) /* ASCII: result = ascii value */
+ break;
+#endif
+
+ if (_DBUS_UNLIKELY (!UNICODE_VALID (result))) /* ASCII: always valid */
+ break;
+
+ /* UNICODE_VALID should have caught it */
+ _dbus_assert (result != (dbus_unichar_t)-1);
+
+ p += char_len;
+ }
+
+ /* See that we covered the entire length if a length was
+ * passed in
+ */
+ if (_DBUS_UNLIKELY (p != end))
+ return FALSE;
+ else
+ return TRUE;
+}
+
+/**
+ * Checks that the given range of the string is all nul bytes. If the
+ * given range is not entirely contained in the string, returns
+ * #FALSE.
+ *
+ * @todo this is inconsistent with most of DBusString in that
+ * it allows a start,len range that extends past the string end.
+ *
+ * @param str the string
+ * @param start first byte index to check
+ * @param len number of bytes to check
+ * @returns #TRUE if the byte range exists and is all nul bytes
+ */
+dbus_bool_t
+_dbus_string_validate_nul (const DBusString *str,
+ int start,
+ int len)
+{
+ const unsigned char *s;
+ const unsigned char *end;
+ DBUS_CONST_STRING_PREAMBLE (str);
+ _dbus_assert (start >= 0);
+ _dbus_assert (len >= 0);
+ _dbus_assert (start <= real->len);
+
+ if (len > real->len - start)
+ return FALSE;
+
+ s = real->str + start;
+ end = s + len;
+ while (s != end)
+ {
+ if (_DBUS_UNLIKELY (*s != '\0'))
+ return FALSE;
+ ++s;
+ }
+
+ return TRUE;
+}
+
+/**
+ * Clears all allocated bytes in the string to zero.
+ *
+ * @param str the string
+ */
+void
+_dbus_string_zero (DBusString *str)
+{
+ DBUS_STRING_PREAMBLE (str);
+
+ memset (real->str - real->align_offset, '\0', real->allocated);
+}
+/** @} */
+
+/* tests are in dbus-string-util.c */
diff --git a/src/3rdparty/libdbus/dbus/dbus-string.h b/src/3rdparty/libdbus/dbus/dbus-string.h
new file mode 100644
index 00000000..be2434db
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-string.h
@@ -0,0 +1,454 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-string.h String utility class (internal to D-Bus implementation)
+ *
+ * Copyright (C) 2002, 2003 Red Hat, Inc.
+ * Copyright (C) 2006 Ralf Habacker <ralf.habacker@freenet.de>
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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
+ *
+ */
+
+#ifndef DBUS_STRING_H
+#define DBUS_STRING_H
+
+#include <dbus/dbus-macros.h>
+#include <dbus/dbus-types.h>
+#include <dbus/dbus-memory.h>
+
+#include <stdarg.h>
+
+#include <dbus/dbus-macros-internal.h>
+
+DBUS_BEGIN_DECLS
+
+/**
+ * DBusString object
+ */
+
+typedef struct DBusString DBusString;
+
+struct DBusString
+{
+#if defined(DBUS_WIN) && defined(_DEBUG)
+ const char *dummy1; /**< placeholder */
+#else
+ const void *dummy1; /**< placeholder */
+#endif
+ int dummy2; /**< placeholder */
+ int dummy3; /**< placeholder */
+ unsigned int dummy_bit1 : 1; /**< placeholder */
+ unsigned int dummy_bit2 : 1; /**< placeholder */
+ unsigned int dummy_bit3 : 1; /**< placeholder */
+ unsigned int dummy_bits : 3; /**< placeholder */
+};
+
+/**
+ * Content for a DBusString that is considered invalid for all
+ * operations, except that it is valid to call _dbus_string_free()
+ * during error handling.
+ */
+#define _DBUS_STRING_INIT_INVALID \
+{ \
+ NULL, /* dummy1 */ \
+ 0, /* dummy2 */ \
+ 0, /* dummy3 */ \
+ 0, /* dummy_bit1 */ \
+ 0, /* dummy_bit2 */ \
+ 0, /* dummy_bit3 */ \
+ 0 /* dummy_bits */ \
+}
+
+#ifdef DBUS_DISABLE_ASSERT
+/* Some simple inlining hacks; the current linker is not smart enough
+ * to inline non-exported symbols across files in the library.
+ * Note that these break type safety (due to the casts)
+ */
+#define _dbus_string_get_data(s) ((char*)(((DBusString*)(s))->dummy1))
+#define _dbus_string_get_length(s) (((DBusString*)(s))->dummy2)
+#define _dbus_string_set_byte(s, i, b) ((((unsigned char*)(((DBusString*)(s))->dummy1))[(i)]) = (unsigned char) (b))
+#define _dbus_string_get_byte(s, i) (((const unsigned char*)(((DBusString*)(s))->dummy1))[(i)])
+#define _dbus_string_get_const_data(s) ((const char*)(((DBusString*)(s))->dummy1))
+#define _dbus_string_get_const_data_len(s,start,len) (((const char*)(((DBusString*)(s))->dummy1)) + (start))
+#endif
+
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_string_init (DBusString *str);
+DBUS_PRIVATE_EXPORT
+void _dbus_string_init_const (DBusString *str,
+ const char *value);
+DBUS_PRIVATE_EXPORT
+void _dbus_string_init_const_len (DBusString *str,
+ const char *value,
+ int len);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_string_init_preallocated (DBusString *str,
+ int allocate_size);
+
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_string_init_from_string (DBusString *str,
+ const DBusString *from);
+DBUS_PRIVATE_EXPORT
+void _dbus_string_free (DBusString *str);
+void _dbus_string_lock (DBusString *str);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_string_compact (DBusString *str,
+ int max_waste);
+DBUS_PRIVATE_EXPORT
+int _dbus_string_get_allocated_size (const DBusString *str);
+#ifndef _dbus_string_get_data
+DBUS_PRIVATE_EXPORT
+char* _dbus_string_get_data (DBusString *str);
+#endif /* _dbus_string_get_data */
+#ifndef _dbus_string_get_const_data
+DBUS_PRIVATE_EXPORT
+const char* _dbus_string_get_const_data (const DBusString *str);
+#endif /* _dbus_string_get_const_data */
+DBUS_PRIVATE_EXPORT
+char* _dbus_string_get_data_len (DBusString *str,
+ int start,
+ int len);
+#ifndef _dbus_string_get_const_data_len
+DBUS_PRIVATE_EXPORT
+const char* _dbus_string_get_const_data_len (const DBusString *str,
+ int start,
+ int len);
+#endif
+#ifndef _dbus_string_set_byte
+DBUS_PRIVATE_EXPORT
+void _dbus_string_set_byte (DBusString *str,
+ int i,
+ unsigned char byte);
+#endif
+#ifndef _dbus_string_get_byte
+DBUS_PRIVATE_EXPORT
+unsigned char _dbus_string_get_byte (const DBusString *str,
+ int start);
+#endif /* _dbus_string_get_byte */
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_string_insert_bytes (DBusString *str,
+ int i,
+ int n_bytes,
+ unsigned char byte);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_string_insert_byte (DBusString *str,
+ int i,
+ unsigned char byte);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_string_steal_data (DBusString *str,
+ char **data_return);
+dbus_bool_t _dbus_string_steal_data_len (DBusString *str,
+ char **data_return,
+ int start,
+ int len);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_string_copy_data (const DBusString *str,
+ char **data_return);
+dbus_bool_t _dbus_string_copy_data_len (const DBusString *str,
+ char **data_return,
+ int start,
+ int len);
+void _dbus_string_copy_to_buffer (const DBusString *str,
+ char *buffer,
+ int len);
+DBUS_PRIVATE_EXPORT
+void _dbus_string_copy_to_buffer_with_nul (const DBusString *str,
+ char *buffer,
+ int avail_len);
+#ifndef _dbus_string_get_length
+DBUS_PRIVATE_EXPORT
+int _dbus_string_get_length (const DBusString *str);
+#endif /* !_dbus_string_get_length */
+
+/**
+ * Get the string's length as an unsigned integer, for comparison with
+ * size_t and similar unsigned types that does not trigger compiler
+ * warnings about potential value changes during conversion.
+ *
+ * DBusString lengths are signed for historical reasons, but we know that
+ * the length is always >= 0 (and DBUS_GENERIC_STRING_PREAMBLE asserts
+ * that this is the case) so we know that this cast does not change the
+ * value.
+ */
+static inline unsigned int
+_dbus_string_get_length_uint (const DBusString *str)
+{
+ return (unsigned int) _dbus_string_get_length (str);
+}
+
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_string_lengthen (DBusString *str,
+ int additional_length);
+DBUS_PRIVATE_EXPORT
+void _dbus_string_shorten (DBusString *str,
+ int length_to_remove);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_string_set_length (DBusString *str,
+ int length);
+dbus_bool_t _dbus_string_align_length (DBusString *str,
+ int alignment);
+dbus_bool_t _dbus_string_alloc_space (DBusString *str,
+ int extra_bytes);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_string_append (DBusString *str,
+ const char *buffer);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_string_append_len (DBusString *str,
+ const char *buffer,
+ int len);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_string_append_int (DBusString *str,
+ long value);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_string_append_uint (DBusString *str,
+ unsigned long value);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_string_append_byte (DBusString *str,
+ unsigned char byte);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_string_append_strings (DBusString *str,
+ char **strings,
+ char separator);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_string_append_printf (DBusString *str,
+ const char *format,
+ ...) _DBUS_GNUC_PRINTF (2, 3);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_string_append_printf_valist (DBusString *str,
+ const char *format,
+ va_list args) _DBUS_GNUC_PRINTF (2, 0);
+dbus_bool_t _dbus_string_insert_2_aligned (DBusString *str,
+ int insert_at,
+ const unsigned char octets[2]);
+dbus_bool_t _dbus_string_insert_4_aligned (DBusString *str,
+ int insert_at,
+ const unsigned char octets[4]);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_string_insert_8_aligned (DBusString *str,
+ int insert_at,
+ const unsigned char octets[8]);
+dbus_bool_t _dbus_string_insert_alignment (DBusString *str,
+ int *insert_at,
+ int alignment);
+DBUS_PRIVATE_EXPORT
+void _dbus_string_delete (DBusString *str,
+ int start,
+ int len);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_string_move (DBusString *source,
+ int start,
+ DBusString *dest,
+ int insert_at);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_string_copy (const DBusString *source,
+ int start,
+ DBusString *dest,
+ int insert_at);
+dbus_bool_t _dbus_string_move_len (DBusString *source,
+ int start,
+ int len,
+ DBusString *dest,
+ int insert_at);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_string_copy_len (const DBusString *source,
+ int start,
+ int len,
+ DBusString *dest,
+ int insert_at);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_string_replace_len (const DBusString *source,
+ int start,
+ int len,
+ DBusString *dest,
+ int replace_at,
+ int replace_len);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_string_split_on_byte (DBusString *source,
+ unsigned char byte,
+ DBusString *tail);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_string_parse_int (const DBusString *str,
+ int start,
+ long *value_return,
+ int *end_return);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_string_parse_uint (const DBusString *str,
+ int start,
+ unsigned long *value_return,
+ int *end_return);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_string_parse_int64 (const DBusString *str,
+ int start,
+ dbus_int64_t *value_return,
+ int *end_return);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_string_find (const DBusString *str,
+ int start,
+ const char *substr,
+ int *found);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_string_find_eol (const DBusString *str,
+ int start,
+ int *found,
+ int *found_len);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_string_find_to (const DBusString *str,
+ int start,
+ int end,
+ const char *substr,
+ int *found);
+dbus_bool_t _dbus_string_find_byte_backward (const DBusString *str,
+ int start,
+ unsigned char byte,
+ int *found);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_string_find_blank (const DBusString *str,
+ int start,
+ int *found);
+DBUS_PRIVATE_EXPORT
+void _dbus_string_skip_blank (const DBusString *str,
+ int start,
+ int *end);
+DBUS_PRIVATE_EXPORT
+void _dbus_string_skip_white (const DBusString *str,
+ int start,
+ int *end);
+void _dbus_string_skip_white_reverse (const DBusString *str,
+ int end,
+ int *start);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_string_equal (const DBusString *a,
+ const DBusString *b);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_string_equal_c_str (const DBusString *a,
+ const char *c_str);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_string_equal_len (const DBusString *a,
+ const DBusString *b,
+ int len);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_string_equal_substring (const DBusString *a,
+ int a_start,
+ int a_len,
+ const DBusString *b,
+ int b_start);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_string_starts_with_c_str (const DBusString *a,
+ const char *c_str);
+dbus_bool_t _dbus_string_ends_with_c_str (const DBusString *a,
+ const char *c_str);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_string_starts_with_words_c_str (const DBusString *a,
+ const char *c_str,
+ char word_separator);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_string_pop_line (DBusString *source,
+ DBusString *dest);
+DBUS_PRIVATE_EXPORT
+void _dbus_string_delete_first_word (DBusString *str);
+DBUS_PRIVATE_EXPORT
+void _dbus_string_delete_leading_blanks (DBusString *str);
+DBUS_PRIVATE_EXPORT
+void _dbus_string_chop_white (DBusString *str);
+dbus_bool_t _dbus_string_append_byte_as_hex (DBusString *str,
+ unsigned char byte);
+DBUS_EMBEDDED_TESTS_EXPORT
+dbus_bool_t _dbus_string_append_buffer_as_hex (DBusString *str,
+ void *buf,
+ int size);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_string_hex_encode (const DBusString *source,
+ int start,
+ DBusString *dest,
+ int insert_at);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_string_hex_decode (const DBusString *source,
+ int start,
+ int *end_return,
+ DBusString *dest,
+ int insert_at);
+DBUS_PRIVATE_EXPORT
+void _dbus_string_tolower_ascii (const DBusString *str,
+ int start,
+ int len);
+DBUS_PRIVATE_EXPORT
+void _dbus_string_toupper_ascii (const DBusString *str,
+ int start,
+ int len);
+dbus_bool_t _dbus_string_validate_ascii (const DBusString *str,
+ int start,
+ int len);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_string_validate_utf8 (const DBusString *str,
+ int start,
+ int len);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_string_validate_nul (const DBusString *str,
+ int start,
+ int len);
+void _dbus_string_zero (DBusString *str);
+
+static inline unsigned char *
+_dbus_string_get_udata (DBusString *str)
+{
+ return (unsigned char *) _dbus_string_get_data (str);
+}
+
+static inline unsigned char *
+_dbus_string_get_udata_len (DBusString *str, int start, int len)
+{
+ return (unsigned char *) _dbus_string_get_data_len (str, start, len);
+}
+
+static inline const unsigned char *
+_dbus_string_get_const_udata (const DBusString *str)
+{
+ return (const unsigned char *) _dbus_string_get_const_data (str);
+}
+
+static inline const unsigned char *
+_dbus_string_get_const_udata_len (const DBusString *str, int start, int len)
+{
+ return (const unsigned char *) _dbus_string_get_const_data_len (str, start, len);
+}
+
+/**
+ * We allocate 1 byte for nul termination, plus 7 bytes for possible
+ * align_offset, so we always need 8 bytes on top of the string's
+ * length to be in the allocated block.
+ */
+#define _DBUS_STRING_ALLOCATION_PADDING 8
+
+/**
+ * Defines a static const variable with type #DBusString called "name"
+ * containing the given string literal.
+ *
+ * @param name the name of the variable
+ * @param str the string value
+ */
+#define _DBUS_STRING_DEFINE_STATIC(name, str) \
+ static const char _dbus_static_string_##name[] = str; \
+ static const DBusString name = { _dbus_static_string_##name, \
+ sizeof(_dbus_static_string_##name) - 1, \
+ sizeof(_dbus_static_string_##name) + \
+ _DBUS_STRING_ALLOCATION_PADDING, \
+ TRUE, TRUE, TRUE, 0 }
+
+DBUS_END_DECLS
+
+#endif /* DBUS_STRING_H */
diff --git a/src/3rdparty/libdbus/dbus/dbus-syntax.c b/src/3rdparty/libdbus/dbus/dbus-syntax.c
new file mode 100644
index 00000000..288824fe
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-syntax.c
@@ -0,0 +1,311 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-syntax.c - utility functions for strings with special syntax
+ *
+ * Author: Simon McVittie <simon.mcvittie@collabora.co.uk>
+ * Copyright © 2011 Nokia Corporation
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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-syntax.h"
+
+#include "dbus-internals.h"
+#include "dbus-marshal-validate.h"
+#include "dbus-shared.h"
+
+/**
+ * @defgroup DBusSyntax Utility functions for strings with special syntax
+ * @ingroup DBus
+ * @brief Parsing D-Bus type signatures
+ * @{
+ */
+
+/**
+ * Check an object path for validity. Remember that #NULL can always
+ * be passed instead of a DBusError *, if you don't care about having
+ * an error name and message.
+ *
+ * This function is suitable for validating C strings, but is not suitable
+ * for validating untrusted data from a network unless the string's length
+ * is also checked, since it assumes that the string ends at the first zero
+ * byte according to normal C conventions.
+ *
+ * @param path a potentially invalid object path, which must not be #NULL
+ * @param error error return
+ * @returns #TRUE if path is valid
+ */
+dbus_bool_t
+dbus_validate_path (const char *path,
+ DBusError *error)
+{
+ DBusString str;
+ int len;
+
+ _dbus_return_val_if_fail (path != NULL, FALSE);
+
+ _dbus_string_init_const (&str, path);
+ len = _dbus_string_get_length (&str);
+
+ /* In general, it ought to be valid... */
+ if (_DBUS_LIKELY (_dbus_validate_path (&str, 0, len)))
+ return TRUE;
+
+ /* slow path: string is invalid, find out why */
+
+ if (!_dbus_string_validate_utf8 (&str, 0, len))
+ {
+ /* don't quote the actual string here, since a DBusError also needs to
+ * be valid UTF-8 */
+ dbus_set_error (error, DBUS_ERROR_INVALID_ARGS,
+ "Object path was not valid UTF-8");
+ return FALSE;
+ }
+
+ /* FIXME: later, diagnose exactly how it was invalid */
+ dbus_set_error (error, DBUS_ERROR_INVALID_ARGS,
+ "Object path was not valid: '%s'", path);
+ return FALSE;
+}
+
+/**
+ * Check an interface name for validity. Remember that #NULL can always
+ * be passed instead of a DBusError *, if you don't care about having
+ * an error name and message.
+ *
+ * This function is suitable for validating C strings, but is not suitable
+ * for validating untrusted data from a network unless the string's length
+ * is also checked, since it assumes that the string ends at the first zero
+ * byte according to normal C conventions.
+ *
+ * @param name a potentially invalid interface name, which must not be #NULL
+ * @param error error return
+ * @returns #TRUE if name is valid
+ */
+dbus_bool_t
+dbus_validate_interface (const char *name,
+ DBusError *error)
+{
+ DBusString str;
+ int len;
+
+ _dbus_return_val_if_fail (name != NULL, FALSE);
+
+ _dbus_string_init_const (&str, name);
+ len = _dbus_string_get_length (&str);
+
+ /* In general, it ought to be valid... */
+ if (_DBUS_LIKELY (_dbus_validate_interface (&str, 0, len)))
+ return TRUE;
+
+ /* slow path: string is invalid, find out why */
+
+ if (!_dbus_string_validate_utf8 (&str, 0, len))
+ {
+ /* don't quote the actual string here, since a DBusError also needs to
+ * be valid UTF-8 */
+ dbus_set_error (error, DBUS_ERROR_INVALID_ARGS,
+ "Interface name was not valid UTF-8");
+ return FALSE;
+ }
+
+ /* FIXME: later, diagnose exactly how it was invalid */
+ dbus_set_error (error, DBUS_ERROR_INVALID_ARGS,
+ "Interface name was not valid: '%s'", name);
+ return FALSE;
+}
+
+/**
+ * Check a member (method/signal) name for validity. Remember that #NULL
+ * can always be passed instead of a DBusError *, if you don't care about
+ * having an error name and message.
+ *
+ * This function is suitable for validating C strings, but is not suitable
+ * for validating untrusted data from a network unless the string's length
+ * is also checked, since it assumes that the string ends at the first zero
+ * byte according to normal C conventions.
+ *
+ * @param name a potentially invalid member name, which must not be #NULL
+ * @param error error return
+ * @returns #TRUE if name is valid
+ */
+dbus_bool_t
+dbus_validate_member (const char *name,
+ DBusError *error)
+{
+ DBusString str;
+ int len;
+
+ _dbus_return_val_if_fail (name != NULL, FALSE);
+
+ _dbus_string_init_const (&str, name);
+ len = _dbus_string_get_length (&str);
+
+ /* In general, it ought to be valid... */
+ if (_DBUS_LIKELY (_dbus_validate_member (&str, 0, len)))
+ return TRUE;
+
+ /* slow path: string is invalid, find out why */
+
+ if (!_dbus_string_validate_utf8 (&str, 0, len))
+ {
+ /* don't quote the actual string here, since a DBusError also needs to
+ * be valid UTF-8 */
+ dbus_set_error (error, DBUS_ERROR_INVALID_ARGS,
+ "Member name was not valid UTF-8");
+ return FALSE;
+ }
+
+ /* FIXME: later, diagnose exactly how it was invalid */
+ dbus_set_error (error, DBUS_ERROR_INVALID_ARGS,
+ "Member name was not valid: '%s'", name);
+ return FALSE;
+}
+
+/**
+ * Check an error name for validity. Remember that #NULL
+ * can always be passed instead of a DBusError *, if you don't care about
+ * having an error name and message.
+ *
+ * This function is suitable for validating C strings, but is not suitable
+ * for validating untrusted data from a network unless the string's length
+ * is also checked, since it assumes that the string ends at the first zero
+ * byte according to normal C conventions.
+ *
+ * @param name a potentially invalid error name, which must not be #NULL
+ * @param error error return
+ * @returns #TRUE if name is valid
+ */
+dbus_bool_t
+dbus_validate_error_name (const char *name,
+ DBusError *error)
+{
+ DBusString str;
+ int len;
+
+ _dbus_return_val_if_fail (name != NULL, FALSE);
+
+ _dbus_string_init_const (&str, name);
+ len = _dbus_string_get_length (&str);
+
+ /* In general, it ought to be valid... */
+ if (_DBUS_LIKELY (_dbus_validate_error_name (&str, 0, len)))
+ return TRUE;
+
+ /* slow path: string is invalid, find out why */
+
+ if (!_dbus_string_validate_utf8 (&str, 0, len))
+ {
+ /* don't quote the actual string here, since a DBusError also needs to
+ * be valid UTF-8 */
+ dbus_set_error (error, DBUS_ERROR_INVALID_ARGS,
+ "Error name was not valid UTF-8");
+ return FALSE;
+ }
+
+ /* FIXME: later, diagnose exactly how it was invalid */
+ dbus_set_error (error, DBUS_ERROR_INVALID_ARGS,
+ "Error name was not valid: '%s'", name);
+ return FALSE;
+}
+
+/**
+ * Check a bus name for validity. Remember that #NULL
+ * can always be passed instead of a DBusError *, if you don't care about
+ * having an error name and message.
+ *
+ * This function is suitable for validating C strings, but is not suitable
+ * for validating untrusted data from a network unless the string's length
+ * is also checked, since it assumes that the string ends at the first zero
+ * byte according to normal C conventions.
+ *
+ * @param name a potentially invalid bus name, which must not be #NULL
+ * @param error error return
+ * @returns #TRUE if name is valid
+ */
+dbus_bool_t
+dbus_validate_bus_name (const char *name,
+ DBusError *error)
+{
+ DBusString str;
+ int len;
+
+ _dbus_return_val_if_fail (name != NULL, FALSE);
+
+ _dbus_string_init_const (&str, name);
+ len = _dbus_string_get_length (&str);
+
+ /* In general, it ought to be valid... */
+ if (_DBUS_LIKELY (_dbus_validate_bus_name (&str, 0, len)))
+ return TRUE;
+
+ /* slow path: string is invalid, find out why */
+
+ if (!_dbus_string_validate_utf8 (&str, 0, len))
+ {
+ /* don't quote the actual string here, since a DBusError also needs to
+ * be valid UTF-8 */
+ dbus_set_error (error, DBUS_ERROR_INVALID_ARGS,
+ "Bus name was not valid UTF-8");
+ return FALSE;
+ }
+
+ /* FIXME: later, diagnose exactly how it was invalid */
+ dbus_set_error (error, DBUS_ERROR_INVALID_ARGS,
+ "Bus name was not valid: '%s'", name);
+ return FALSE;
+}
+
+/**
+ * Check a string for validity. Strings on D-Bus must be valid UTF-8.
+ * Remember that #NULL can always be passed instead of a DBusError *,
+ * if you don't care about having an error name and message.
+ *
+ * This function is suitable for validating C strings, but is not suitable
+ * for validating untrusted data from a network unless the string's length
+ * is also checked, since it assumes that the string ends at the first zero
+ * byte according to normal C conventions.
+ *
+ * @param alleged_utf8 a string to be checked, which must not be #NULL
+ * @param error error return
+ * @returns #TRUE if alleged_utf8 is valid UTF-8
+ */
+dbus_bool_t
+dbus_validate_utf8 (const char *alleged_utf8,
+ DBusError *error)
+{
+ DBusString str;
+
+ _dbus_return_val_if_fail (alleged_utf8 != NULL, FALSE);
+
+ _dbus_string_init_const (&str, alleged_utf8);
+
+ if (_DBUS_LIKELY (_dbus_string_validate_utf8 (&str, 0,
+ _dbus_string_get_length (&str))))
+ return TRUE;
+
+ /* don't quote the actual string here, since a DBusError also needs to
+ * be valid UTF-8 */
+ dbus_set_error (error, DBUS_ERROR_INVALID_ARGS,
+ "String was not valid UTF-8");
+ return FALSE;
+}
+
+/** @} */ /* end of group */
diff --git a/src/3rdparty/libdbus/dbus/dbus-syntax.h b/src/3rdparty/libdbus/dbus/dbus-syntax.h
new file mode 100644
index 00000000..57459b17
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-syntax.h
@@ -0,0 +1,60 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-syntax.h - utility functions for strings with special syntax
+ *
+ * Author: Simon McVittie <simon.mcvittie@collabora.co.uk>
+ * Copyright © 2011 Nokia Corporation
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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
+ *
+ */
+#if !defined (DBUS_INSIDE_DBUS_H) && !defined (DBUS_COMPILATION)
+#error "Only <dbus/dbus.h> can be included directly, this file may disappear or change contents."
+#endif
+
+#ifndef DBUS_SYNTAX_H
+#define DBUS_SYNTAX_H
+
+#include <dbus/dbus-macros.h>
+#include <dbus/dbus-types.h>
+#include <dbus/dbus-errors.h>
+
+DBUS_BEGIN_DECLS
+
+DBUS_EXPORT
+dbus_bool_t dbus_validate_path (const char *path,
+ DBusError *error);
+DBUS_EXPORT
+dbus_bool_t dbus_validate_interface (const char *name,
+ DBusError *error);
+DBUS_EXPORT
+dbus_bool_t dbus_validate_member (const char *name,
+ DBusError *error);
+DBUS_EXPORT
+dbus_bool_t dbus_validate_error_name (const char *name,
+ DBusError *error);
+DBUS_EXPORT
+dbus_bool_t dbus_validate_bus_name (const char *name,
+ DBusError *error);
+DBUS_EXPORT
+dbus_bool_t dbus_validate_utf8 (const char *alleged_utf8,
+ DBusError *error);
+
+DBUS_END_DECLS
+
+#endif /* multiple-inclusion guard */
diff --git a/src/3rdparty/libdbus/dbus/dbus-sysdeps-pthread.c b/src/3rdparty/libdbus/dbus/dbus-sysdeps-pthread.c
new file mode 100644
index 00000000..f9c25604
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-sysdeps-pthread.c
@@ -0,0 +1,322 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-sysdeps-pthread.c Implements threads using pthreads (internal to libdbus)
+ *
+ * Copyright (C) 2002, 2003, 2006 Red Hat, Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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-sysdeps.h"
+#include "dbus-threads.h"
+
+#include <sys/time.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <string.h>
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include <config.h>
+
+#ifdef HAVE_MONOTONIC_CLOCK
+/* Whether we have a "monotonic" clock; i.e. a clock not affected by
+ * changes in system time.
+ * This is initialized once in check_monotonic_clock below.
+ * https://bugs.freedesktop.org/show_bug.cgi?id=18121
+ */
+static dbus_bool_t have_monotonic_clock = 0;
+#endif
+
+struct DBusRMutex {
+ pthread_mutex_t lock; /**< the lock */
+};
+
+struct DBusCMutex {
+ pthread_mutex_t lock; /**< the lock */
+};
+
+struct DBusCondVar {
+ pthread_cond_t cond; /**< the condition */
+};
+
+#define DBUS_MUTEX(m) ((DBusMutex*) m)
+#define DBUS_MUTEX_PTHREAD(m) ((DBusMutexPThread*) m)
+
+#define DBUS_COND_VAR(c) ((DBusCondVar*) c)
+#define DBUS_COND_VAR_PTHREAD(c) ((DBusCondVarPThread*) c)
+
+
+#ifdef DBUS_DISABLE_ASSERT
+/* (tmp != 0) is a no-op usage to silence compiler */
+#define PTHREAD_CHECK(func_name, result_or_call) \
+ do { int tmp = (result_or_call); if (tmp != 0) {;} } while (0)
+#else
+#define PTHREAD_CHECK(func_name, result_or_call) do { \
+ int tmp = (result_or_call); \
+ if (tmp != 0) { \
+ _dbus_warn_check_failed ("pthread function %s failed with %d %s in %s", \
+ func_name, tmp, strerror(tmp), _DBUS_FUNCTION_NAME); \
+ } \
+} while (0)
+#endif /* !DBUS_DISABLE_ASSERT */
+
+DBusCMutex *
+_dbus_platform_cmutex_new (void)
+{
+ DBusCMutex *pmutex;
+ int result;
+
+ pmutex = dbus_new (DBusCMutex, 1);
+ if (pmutex == NULL)
+ return NULL;
+
+ result = pthread_mutex_init (&pmutex->lock, NULL);
+
+ if (result == ENOMEM || result == EAGAIN)
+ {
+ dbus_free (pmutex);
+ return NULL;
+ }
+ else
+ {
+ PTHREAD_CHECK ("pthread_mutex_init", result);
+ }
+
+ return pmutex;
+}
+
+DBusRMutex *
+_dbus_platform_rmutex_new (void)
+{
+ DBusRMutex *pmutex;
+ pthread_mutexattr_t mutexattr;
+ int result;
+
+ pmutex = dbus_new (DBusRMutex, 1);
+ if (pmutex == NULL)
+ return NULL;
+
+ pthread_mutexattr_init (&mutexattr);
+ pthread_mutexattr_settype (&mutexattr, PTHREAD_MUTEX_RECURSIVE);
+ result = pthread_mutex_init (&pmutex->lock, &mutexattr);
+ pthread_mutexattr_destroy (&mutexattr);
+
+ if (result == ENOMEM || result == EAGAIN)
+ {
+ dbus_free (pmutex);
+ return NULL;
+ }
+ else
+ {
+ PTHREAD_CHECK ("pthread_mutex_init", result);
+ }
+
+ return pmutex;
+}
+
+void
+_dbus_platform_cmutex_free (DBusCMutex *mutex)
+{
+ PTHREAD_CHECK ("pthread_mutex_destroy", pthread_mutex_destroy (&mutex->lock));
+ dbus_free (mutex);
+}
+
+void
+_dbus_platform_rmutex_free (DBusRMutex *mutex)
+{
+ PTHREAD_CHECK ("pthread_mutex_destroy", pthread_mutex_destroy (&mutex->lock));
+ dbus_free (mutex);
+}
+
+void
+_dbus_platform_cmutex_lock (DBusCMutex *mutex)
+{
+ PTHREAD_CHECK ("pthread_mutex_lock", pthread_mutex_lock (&mutex->lock));
+}
+
+void
+_dbus_platform_rmutex_lock (DBusRMutex *mutex)
+{
+ PTHREAD_CHECK ("pthread_mutex_lock", pthread_mutex_lock (&mutex->lock));
+}
+
+void
+_dbus_platform_cmutex_unlock (DBusCMutex *mutex)
+{
+ PTHREAD_CHECK ("pthread_mutex_unlock", pthread_mutex_unlock (&mutex->lock));
+}
+
+void
+_dbus_platform_rmutex_unlock (DBusRMutex *mutex)
+{
+ PTHREAD_CHECK ("pthread_mutex_unlock", pthread_mutex_unlock (&mutex->lock));
+}
+
+DBusCondVar *
+_dbus_platform_condvar_new (void)
+{
+ DBusCondVar *pcond;
+ pthread_condattr_t attr;
+ int result;
+
+ pcond = dbus_new (DBusCondVar, 1);
+ if (pcond == NULL)
+ return NULL;
+
+ pthread_condattr_init (&attr);
+#ifdef HAVE_MONOTONIC_CLOCK
+ if (have_monotonic_clock)
+ pthread_condattr_setclock (&attr, CLOCK_MONOTONIC);
+#endif
+
+ result = pthread_cond_init (&pcond->cond, &attr);
+ pthread_condattr_destroy (&attr);
+
+ if (result == EAGAIN || result == ENOMEM)
+ {
+ dbus_free (pcond);
+ return NULL;
+ }
+ else
+ {
+ PTHREAD_CHECK ("pthread_cond_init", result);
+ }
+
+ return pcond;
+}
+
+void
+_dbus_platform_condvar_free (DBusCondVar *cond)
+{
+ PTHREAD_CHECK ("pthread_cond_destroy", pthread_cond_destroy (&cond->cond));
+ dbus_free (cond);
+}
+
+void
+_dbus_platform_condvar_wait (DBusCondVar *cond,
+ DBusCMutex *mutex)
+{
+ PTHREAD_CHECK ("pthread_cond_wait", pthread_cond_wait (&cond->cond, &mutex->lock));
+}
+
+dbus_bool_t
+_dbus_platform_condvar_wait_timeout (DBusCondVar *cond,
+ DBusCMutex *mutex,
+ int timeout_milliseconds)
+{
+ struct timeval time_now;
+ struct timespec end_time;
+ int result;
+
+#ifdef HAVE_MONOTONIC_CLOCK
+ if (have_monotonic_clock)
+ {
+ struct timespec monotonic_timer;
+ clock_gettime (CLOCK_MONOTONIC,&monotonic_timer);
+ time_now.tv_sec = monotonic_timer.tv_sec;
+ time_now.tv_usec = monotonic_timer.tv_nsec / 1000;
+ }
+ else
+ /* This else falls through to gettimeofday */
+#endif
+ gettimeofday (&time_now, NULL);
+
+ end_time.tv_sec = time_now.tv_sec + timeout_milliseconds / 1000;
+ end_time.tv_nsec = (time_now.tv_usec + (timeout_milliseconds % 1000) * 1000) * 1000;
+ if (end_time.tv_nsec > 1000*1000*1000)
+ {
+ end_time.tv_sec += 1;
+ end_time.tv_nsec -= 1000*1000*1000;
+ }
+
+ result = pthread_cond_timedwait (&cond->cond, &mutex->lock, &end_time);
+
+ if (result != ETIMEDOUT)
+ {
+ PTHREAD_CHECK ("pthread_cond_timedwait", result);
+ }
+
+ /* return true if we did not time out */
+ return result != ETIMEDOUT;
+}
+
+void
+_dbus_platform_condvar_wake_one (DBusCondVar *cond)
+{
+ PTHREAD_CHECK ("pthread_cond_signal", pthread_cond_signal (&cond->cond));
+}
+
+static void
+check_monotonic_clock (void)
+{
+#ifdef HAVE_MONOTONIC_CLOCK
+ struct timespec dummy;
+ if (clock_getres (CLOCK_MONOTONIC, &dummy) == 0)
+ have_monotonic_clock = TRUE;
+#endif
+}
+
+dbus_bool_t
+_dbus_threads_init_platform_specific (void)
+{
+ /* These have static variables, and we need to handle both the case
+ * where dbus_threads_init() has been called and when it hasn't;
+ * so initialize them before any threads are allowed to enter.
+ */
+ check_monotonic_clock ();
+ (void) _dbus_check_setuid ();
+
+ return TRUE;
+}
+
+static pthread_mutex_t init_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+void
+_dbus_threads_lock_platform_specific (void)
+{
+ pthread_mutex_lock (&init_mutex);
+}
+
+void
+_dbus_threads_unlock_platform_specific (void)
+{
+ pthread_mutex_unlock (&init_mutex);
+}
+
+#ifdef DBUS_ENABLE_VERBOSE_MODE
+/*
+ * If we can identify the current process and/or thread, print them to stderr followed by a colon.
+ */
+void
+_dbus_print_thread (void)
+{
+#ifdef __linux__
+ /* we know a pthread_t is numeric on Linux */
+ fprintf (stderr, "%lu: 0x%lx: ", _dbus_pid_for_log (), (unsigned long) pthread_self ());
+#else
+ /* in principle pthread_t isn't required to be printable */
+ fprintf (stderr, "%lu: ", _dbus_pid_for_log ());
+#endif
+}
+#endif
diff --git a/src/3rdparty/libdbus/dbus/dbus-sysdeps-thread-win.c b/src/3rdparty/libdbus/dbus/dbus-sysdeps-thread-win.c
new file mode 100644
index 00000000..9f1818f9
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-sysdeps-thread-win.c
@@ -0,0 +1,339 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-sysdeps-pthread.c Implements threads using Windows threads (internal to libdbus)
+ *
+ * Copyright (C) 2006 Red Hat, Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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-init-win.h"
+#include "dbus-internals.h"
+#include "dbus-sysdeps.h"
+#include "dbus-sysdeps-win.h"
+#include "dbus-threads.h"
+#include "dbus-list.h"
+
+#include <stdio.h>
+
+#include <windows.h>
+
+#ifdef DBUS_DISABLE_ASSERT
+#define THREAD_CHECK_TRUE(func_name, result_or_call) \
+ do { if (!(result_or_call)) { /* ignore */ } } while (0)
+#else
+#define THREAD_CHECK_TRUE(func_name, result_or_call) do { \
+ if (!(result_or_call)) { \
+ _dbus_warn_check_failed ("thread function %s failed (windows error code=%ld) in %s", \
+ func_name, GetLastError (), _DBUS_FUNCTION_NAME); \
+ } \
+} while (0)
+#endif /* !DBUS_DISABLE_ASSERT */
+
+/* Protected by DllMain lock, effectively */
+static dbus_bool_t global_init_done = FALSE;
+static CRITICAL_SECTION init_lock;
+
+/* Called from C++ code in dbus-init-win.cpp. */
+void
+_dbus_threads_windows_init_global (void)
+{
+ /* this ensures that the object that acts as our global constructor
+ * actually gets linked in when we're linked statically */
+ _dbus_threads_windows_ensure_ctor_linked ();
+
+ InitializeCriticalSection (&init_lock);
+ global_init_done = TRUE;
+}
+
+struct DBusCondVar {
+ DBusList *list; /**< list thread-local-stored events waiting on the cond variable */
+ CRITICAL_SECTION lock; /**< lock protecting the list */
+};
+
+static DWORD dbus_cond_event_tls = TLS_OUT_OF_INDEXES;
+
+/* Protected by DllMain lock, effectively */
+static HMODULE dbus_dll_hmodule;
+
+void *
+_dbus_win_get_dll_hmodule (void)
+{
+ return dbus_dll_hmodule;
+}
+
+#ifdef DBUS_WINCE
+#define hinst_t HANDLE
+#else
+#define hinst_t HINSTANCE
+#endif
+
+BOOL WINAPI DllMain (hinst_t, DWORD, LPVOID);
+
+/* We need this to free the TLS events on thread exit */
+BOOL WINAPI
+DllMain (hinst_t hinstDLL,
+ DWORD fdwReason,
+ LPVOID lpvReserved)
+{
+ HANDLE event;
+ switch (fdwReason)
+ {
+ case DLL_PROCESS_ATTACH:
+ dbus_dll_hmodule = hinstDLL;
+ break;
+ case DLL_THREAD_DETACH:
+ if (dbus_cond_event_tls != TLS_OUT_OF_INDEXES)
+ {
+ event = TlsGetValue(dbus_cond_event_tls);
+ CloseHandle (event);
+ TlsSetValue(dbus_cond_event_tls, NULL);
+ }
+ break;
+ case DLL_PROCESS_DETACH:
+ if (dbus_cond_event_tls != TLS_OUT_OF_INDEXES)
+ {
+ event = TlsGetValue(dbus_cond_event_tls);
+ CloseHandle (event);
+ TlsSetValue(dbus_cond_event_tls, NULL);
+
+ TlsFree(dbus_cond_event_tls);
+ }
+ break;
+ default:
+ break;
+ }
+ return TRUE;
+}
+
+DBusCMutex *
+_dbus_platform_cmutex_new (void)
+{
+ HANDLE handle;
+ handle = CreateMutex (NULL, FALSE, NULL);
+ THREAD_CHECK_TRUE ("CreateMutex", handle);
+ return (DBusCMutex *) handle;
+}
+
+DBusRMutex *
+_dbus_platform_rmutex_new (void)
+{
+ HANDLE handle;
+ handle = CreateMutex (NULL, FALSE, NULL);
+ THREAD_CHECK_TRUE ("CreateMutex", handle);
+ return (DBusRMutex *) handle;
+}
+
+DBusRMutex *
+_dbus_win_rmutex_named_new (const char *name)
+{
+ HANDLE handle;
+ handle = CreateMutex (NULL, FALSE, name);
+ THREAD_CHECK_TRUE ("CreateMutex", handle);
+ return (DBusRMutex *) handle;
+}
+
+void
+_dbus_platform_cmutex_free (DBusCMutex *mutex)
+{
+ THREAD_CHECK_TRUE ("CloseHandle", CloseHandle ((HANDLE *) mutex));
+}
+
+void
+_dbus_platform_rmutex_free (DBusRMutex *mutex)
+{
+ THREAD_CHECK_TRUE ("CloseHandle", CloseHandle ((HANDLE *) mutex));
+}
+
+void
+_dbus_platform_cmutex_lock (DBusCMutex *mutex)
+{
+ THREAD_CHECK_TRUE ("WaitForSingleObject", WaitForSingleObject ((HANDLE *) mutex, INFINITE) == WAIT_OBJECT_0);
+}
+
+void
+_dbus_platform_rmutex_lock (DBusRMutex *mutex)
+{
+ THREAD_CHECK_TRUE ("WaitForSingleObject", WaitForSingleObject ((HANDLE *) mutex, INFINITE) == WAIT_OBJECT_0);
+}
+
+void
+_dbus_platform_cmutex_unlock (DBusCMutex *mutex)
+{
+ THREAD_CHECK_TRUE ("ReleaseMutex", ReleaseMutex ((HANDLE *) mutex));
+}
+
+void
+_dbus_platform_rmutex_unlock (DBusRMutex *mutex)
+{
+ THREAD_CHECK_TRUE ("ReleaseMutex", ReleaseMutex ((HANDLE *) mutex));
+}
+
+DBusCondVar *
+_dbus_platform_condvar_new (void)
+{
+ DBusCondVar *cond;
+
+ cond = dbus_new (DBusCondVar, 1);
+ if (cond == NULL)
+ return NULL;
+
+ cond->list = NULL;
+
+ InitializeCriticalSection (&cond->lock);
+ return cond;
+}
+
+void
+_dbus_platform_condvar_free (DBusCondVar *cond)
+{
+ DeleteCriticalSection (&cond->lock);
+ _dbus_list_clear (&cond->list);
+ dbus_free (cond);
+}
+
+static dbus_bool_t
+_dbus_condvar_wait_win32 (DBusCondVar *cond,
+ DBusCMutex *mutex,
+ int milliseconds)
+{
+ DWORD retval;
+ dbus_bool_t ret;
+ HANDLE event = TlsGetValue (dbus_cond_event_tls);
+
+ if (!event)
+ {
+ event = CreateEvent (0, FALSE, FALSE, NULL);
+ if (event == 0)
+ return FALSE;
+ TlsSetValue (dbus_cond_event_tls, event);
+ }
+
+ EnterCriticalSection (&cond->lock);
+
+ /* The event must not be signaled. Check this */
+ _dbus_assert (WaitForSingleObject (event, 0) == WAIT_TIMEOUT);
+
+ ret = _dbus_list_append (&cond->list, event);
+
+ LeaveCriticalSection (&cond->lock);
+
+ if (!ret)
+ return FALSE; /* Prepend failed */
+
+ _dbus_platform_cmutex_unlock (mutex);
+ retval = WaitForSingleObject (event, milliseconds);
+ _dbus_platform_cmutex_lock (mutex);
+
+ if (retval == WAIT_TIMEOUT)
+ {
+ EnterCriticalSection (&cond->lock);
+ _dbus_list_remove (&cond->list, event);
+
+ /* In the meantime we could have been signaled, so we must again
+ * wait for the signal, this time with no timeout, to reset
+ * it. retval is set again to honour the late arrival of the
+ * signal */
+ retval = WaitForSingleObject (event, 0);
+
+ LeaveCriticalSection (&cond->lock);
+ }
+
+#ifndef DBUS_DISABLE_ASSERT
+ EnterCriticalSection (&cond->lock);
+
+ /* Now event must not be inside the array, check this */
+ _dbus_assert (_dbus_list_remove (&cond->list, event) == FALSE);
+
+ LeaveCriticalSection (&cond->lock);
+#endif /* !G_DISABLE_ASSERT */
+
+ return retval != WAIT_TIMEOUT;
+}
+
+void
+_dbus_platform_condvar_wait (DBusCondVar *cond,
+ DBusCMutex *mutex)
+{
+ _dbus_condvar_wait_win32 (cond, mutex, INFINITE);
+}
+
+dbus_bool_t
+_dbus_platform_condvar_wait_timeout (DBusCondVar *cond,
+ DBusCMutex *mutex,
+ int timeout_milliseconds)
+{
+ return _dbus_condvar_wait_win32 (cond, mutex, timeout_milliseconds);
+}
+
+void
+_dbus_platform_condvar_wake_one (DBusCondVar *cond)
+{
+ EnterCriticalSection (&cond->lock);
+
+ if (cond->list != NULL)
+ {
+ SetEvent (_dbus_list_pop_first (&cond->list));
+ /* Avoid live lock by pushing the waiter to the mutex lock
+ instruction, which is fair. If we don't do this, we could
+ acquire the condition variable again before the waiter has a
+ chance itself, leading to starvation. */
+ Sleep (0);
+ }
+ LeaveCriticalSection (&cond->lock);
+}
+
+dbus_bool_t
+_dbus_threads_init_platform_specific (void)
+{
+ /* We reuse this over several generations, because we can't
+ * free the events once they are in use
+ */
+ if (dbus_cond_event_tls == TLS_OUT_OF_INDEXES)
+ {
+ dbus_cond_event_tls = TlsAlloc ();
+ if (dbus_cond_event_tls == TLS_OUT_OF_INDEXES)
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+void
+_dbus_threads_lock_platform_specific (void)
+{
+ _dbus_assert (global_init_done);
+ EnterCriticalSection (&init_lock);
+}
+
+void
+_dbus_threads_unlock_platform_specific (void)
+{
+ _dbus_assert (global_init_done);
+ LeaveCriticalSection (&init_lock);
+}
+
+#ifdef DBUS_ENABLE_VERBOSE_MODE
+void
+_dbus_print_thread (void)
+{
+ fprintf (stderr, "%lu: 0x%04lx: ", _dbus_pid_for_log (), GetCurrentThreadId ());
+}
+#endif
diff --git a/src/3rdparty/libdbus/dbus/dbus-sysdeps-unix.c b/src/3rdparty/libdbus/dbus/dbus-sysdeps-unix.c
new file mode 100644
index 00000000..cf99616d
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-sysdeps-unix.c
@@ -0,0 +1,5273 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-sysdeps-unix.c Wrappers around UNIX system/libc features (internal to D-Bus implementation)
+ *
+ * Copyright (C) 2002, 2003, 2006 Red Hat, Inc.
+ * Copyright (C) 2003 CodeFactory AB
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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-sysdeps.h"
+#include "dbus-sysdeps-unix.h"
+#include "dbus-threads.h"
+#include "dbus-protocol.h"
+#include "dbus-file.h"
+#include "dbus-transport.h"
+#include "dbus-string.h"
+#include "dbus-userdb.h"
+#include "dbus-list.h"
+#include "dbus-credentials.h"
+#include "dbus-nonce.h"
+
+#include <limits.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <dirent.h>
+#include <sys/un.h>
+#include <pwd.h>
+#include <time.h>
+#include <locale.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <netdb.h>
+#include <grp.h>
+#include <arpa/inet.h>
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_LINUX_CLOSE_RANGE_H
+#include <linux/close_range.h>
+#endif
+#ifdef HAVE_SYSLOG_H
+#include <syslog.h>
+#endif
+#ifdef HAVE_WRITEV
+#include <sys/uio.h>
+#endif
+#ifdef HAVE_BACKTRACE
+#include <execinfo.h>
+#endif
+#ifdef HAVE_GETPEERUCRED
+#include <ucred.h>
+#endif
+#ifdef HAVE_ALLOCA_H
+#include <alloca.h>
+#endif
+#ifdef HAVE_SYS_RANDOM_H
+#include <sys/random.h>
+#endif
+#ifdef HAVE_SYS_SYSCALL_H
+#include <sys/syscall.h>
+#endif
+
+#ifdef HAVE_ADT
+#include <bsm/adt.h>
+#endif
+
+#ifdef HAVE_SYSTEMD
+#include <systemd/sd-daemon.h>
+#endif
+
+#if !defined(HAVE_STDATOMIC_H) && !DBUS_USE_SYNC
+#include <pthread.h>
+#endif
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+#ifndef AI_ADDRCONFIG
+#define AI_ADDRCONFIG 0
+#endif
+
+#ifndef HAVE_SOCKLEN_T
+#define socklen_t int
+#endif
+
+#if defined (__sun) || defined (__sun__)
+/*
+ * CMS_SPACE etc. definitions for Solaris < 10, based on
+ * http://mailman.videolan.org/pipermail/vlc-devel/2006-May/024402.html
+ * via
+ * http://wiki.opencsw.org/porting-faq#toc10
+ *
+ * These are only redefined for Solaris, for now: if your OS needs these too,
+ * please file a bug. (Or preferably, improve your OS so they're not needed.)
+ */
+
+# ifndef CMSG_ALIGN
+# ifdef __sun__
+# define CMSG_ALIGN(len) _CMSG_DATA_ALIGN (len)
+# else
+ /* aligning to sizeof (long) is assumed to be portable (fd.o#40235) */
+# define CMSG_ALIGN(len) (((len) + sizeof (long) - 1) & \
+ ~(sizeof (long) - 1))
+# endif
+# endif
+
+# ifndef CMSG_SPACE
+# define CMSG_SPACE(len) (CMSG_ALIGN (sizeof (struct cmsghdr)) + \
+ CMSG_ALIGN (len))
+# endif
+
+# ifndef CMSG_LEN
+# define CMSG_LEN(len) (CMSG_ALIGN (sizeof (struct cmsghdr)) + (len))
+# endif
+
+#endif /* Solaris */
+
+#if defined(__linux__) && defined(__NR_close_range) && !defined(HAVE_CLOSE_RANGE)
+/* The kernel headers are new enough to have the close_range syscall,
+ * but glibc isn't new enough to have the syscall wrapper, so call the
+ * syscall directly. */
+static inline int
+close_range (unsigned int first,
+ unsigned int last,
+ int flags)
+{
+ return syscall (__NR_close_range, first, last, flags);
+}
+/* Now we can call that inline wrapper as though it was provided by glibc. */
+#define HAVE_CLOSE_RANGE
+#endif
+
+/**
+ * Ensure that the standard file descriptors stdin, stdout and stderr
+ * are open, by opening /dev/null if necessary.
+ *
+ * This function does not use DBusError, to avoid calling malloc(), so
+ * that it can be used in contexts where an async-signal-safe function
+ * is required (for example after fork()). Instead, on failure it sets
+ * errno and returns something like "Failed to open /dev/null" in
+ * *error_str_p. Callers are expected to combine *error_str_p
+ * with _dbus_strerror (errno) to get a full error report.
+ *
+ * This function can only be called while single-threaded: either during
+ * startup of an executable, or after fork().
+ */
+dbus_bool_t
+_dbus_ensure_standard_fds (DBusEnsureStandardFdsFlags flags,
+ const char **error_str_p)
+{
+ static int const relevant_flag[] = { DBUS_FORCE_STDIN_NULL,
+ DBUS_FORCE_STDOUT_NULL,
+ DBUS_FORCE_STDERR_NULL };
+ /* Should always get replaced with the real error before use */
+ const char *error_str = "Failed mysteriously";
+ int devnull = -1;
+ int saved_errno;
+ /* This function relies on the standard fds having their POSIX values. */
+ _DBUS_STATIC_ASSERT (STDIN_FILENO == 0);
+ _DBUS_STATIC_ASSERT (STDOUT_FILENO == 1);
+ _DBUS_STATIC_ASSERT (STDERR_FILENO == 2);
+ int i;
+
+ for (i = STDIN_FILENO; i <= STDERR_FILENO; i++)
+ {
+ /* Because we rely on being single-threaded, and we want the
+ * standard fds to not be close-on-exec, we don't set it
+ * close-on-exec. */
+ if (devnull < i)
+ devnull = open ("/dev/null", O_RDWR);
+
+ if (devnull < 0)
+ {
+ error_str = "Failed to open /dev/null";
+ goto out;
+ }
+
+ /* We already opened all fds < i, so the only way this assertion
+ * could fail is if another thread closed one, and we document
+ * this function as not safe for multi-threading. */
+ _dbus_assert (devnull >= i);
+
+ if (devnull != i && (flags & relevant_flag[i]) != 0)
+ {
+ if (dup2 (devnull, i) < 0)
+ {
+ error_str = "Failed to dup2 /dev/null onto a standard fd";
+ goto out;
+ }
+ }
+ }
+
+ error_str = NULL;
+
+out:
+ saved_errno = errno;
+
+ if (devnull > STDERR_FILENO)
+ close (devnull);
+
+ if (error_str_p != NULL)
+ *error_str_p = error_str;
+
+ errno = saved_errno;
+ return (error_str == NULL);
+}
+
+static dbus_bool_t _dbus_set_fd_nonblocking (int fd,
+ DBusError *error);
+
+static dbus_bool_t
+_dbus_open_socket (int *fd_p,
+ int domain,
+ int type,
+ int protocol,
+ DBusError *error)
+{
+#ifdef SOCK_CLOEXEC
+ dbus_bool_t cloexec_done;
+
+ *fd_p = socket (domain, type | SOCK_CLOEXEC, protocol);
+ cloexec_done = *fd_p >= 0;
+
+ /* Check if kernel seems to be too old to know SOCK_CLOEXEC */
+ if (*fd_p < 0 && (errno == EINVAL || errno == EPROTOTYPE))
+#endif
+ {
+ *fd_p = socket (domain, type, protocol);
+ }
+
+ if (*fd_p >= 0)
+ {
+#ifdef SOCK_CLOEXEC
+ if (!cloexec_done)
+#endif
+ {
+ _dbus_fd_set_close_on_exec(*fd_p);
+ }
+
+ _dbus_verbose ("socket fd %d opened\n", *fd_p);
+ return TRUE;
+ }
+ else
+ {
+ dbus_set_error(error,
+ _dbus_error_from_errno (errno),
+ "Failed to open socket: %s",
+ _dbus_strerror (errno));
+ return FALSE;
+ }
+}
+
+/**
+ * Opens a UNIX domain socket (as in the socket() call).
+ * Does not bind the socket.
+ *
+ * This will set FD_CLOEXEC for the socket returned
+ *
+ * @param fd return location for socket descriptor
+ * @param error return location for an error
+ * @returns #FALSE if error is set
+ */
+static dbus_bool_t
+_dbus_open_unix_socket (int *fd,
+ DBusError *error)
+{
+ return _dbus_open_socket(fd, AF_UNIX, SOCK_STREAM, 0, error);
+}
+
+/**
+ * Closes a socket and invalidates it. Should not be used on non-socket file
+ * descriptors or handles.
+ *
+ * If an error is detected, this function returns #FALSE and sets the error, but
+ * the socket is still closed and invalidated. Callers can use the error in a
+ * diagnostic message, but should not retry closing the socket.
+ *
+ * @param fd the socket
+ * @param error return location for an error
+ * @returns #FALSE if error is set
+ */
+dbus_bool_t
+_dbus_close_socket (DBusSocket *fd,
+ DBusError *error)
+{
+ dbus_bool_t rv;
+
+ _dbus_assert (fd != NULL);
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ rv = _dbus_close (fd->fd, error);
+ _dbus_socket_invalidate (fd);
+
+ return rv;
+}
+
+/**
+ * Like _dbus_read(), but only works on sockets so is
+ * available on Windows.
+ *
+ * @param fd the socket
+ * @param buffer string to append data to
+ * @param count max amount of data to read
+ * @returns number of bytes appended to the string
+ */
+int
+_dbus_read_socket (DBusSocket fd,
+ DBusString *buffer,
+ int count)
+{
+ return _dbus_read (fd.fd, buffer, count);
+}
+
+/**
+ * Like _dbus_write(), but only supports sockets
+ * and is thus available on Windows.
+ *
+ * @param fd the file descriptor to write
+ * @param buffer the buffer to write data from
+ * @param start the first byte in the buffer to write
+ * @param len the number of bytes to try to write
+ * @returns the number of bytes written or -1 on error
+ */
+int
+_dbus_write_socket (DBusSocket fd,
+ const DBusString *buffer,
+ int start,
+ int len)
+{
+#if HAVE_DECL_MSG_NOSIGNAL
+ const char *data;
+ int bytes_written;
+
+ data = _dbus_string_get_const_data_len (buffer, start, len);
+
+ again:
+
+ bytes_written = send (fd.fd, data, len, MSG_NOSIGNAL);
+
+ if (bytes_written < 0 && errno == EINTR)
+ goto again;
+
+ return bytes_written;
+
+#else
+ return _dbus_write (fd.fd, buffer, start, len);
+#endif
+}
+
+/**
+ * Like _dbus_read_socket() but also tries to read unix fds from the
+ * socket. When there are more fds to read than space in the array
+ * passed this function will fail with ENOSPC.
+ *
+ * @param fd the socket
+ * @param buffer string to append data to
+ * @param count max amount of data to read
+ * @param fds array to place read file descriptors in
+ * @param n_fds on input space in fds array, on output how many fds actually got read
+ * @returns number of bytes appended to string
+ */
+int
+_dbus_read_socket_with_unix_fds (DBusSocket fd,
+ DBusString *buffer,
+ int count,
+ int *fds,
+ unsigned int *n_fds) {
+#ifndef HAVE_UNIX_FD_PASSING
+ int r;
+
+ if ((r = _dbus_read_socket(fd, buffer, count)) < 0)
+ return r;
+
+ *n_fds = 0;
+ return r;
+
+#else
+ int bytes_read;
+ int start;
+ struct msghdr m;
+ struct iovec iov;
+
+ _dbus_assert (count >= 0);
+ _dbus_assert (*n_fds <= DBUS_MAXIMUM_MESSAGE_UNIX_FDS);
+
+ start = _dbus_string_get_length (buffer);
+
+ if (!_dbus_string_lengthen (buffer, count))
+ {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ _DBUS_ZERO(iov);
+ iov.iov_base = _dbus_string_get_data_len (buffer, start, count);
+ iov.iov_len = count;
+
+ _DBUS_ZERO(m);
+ m.msg_iov = &iov;
+ m.msg_iovlen = 1;
+
+ /* Hmm, we have no clue how long the control data will actually be
+ that is queued for us. The least we can do is assume that the
+ caller knows. Hence let's make space for the number of fds that
+ we shall read at max plus the cmsg header. */
+ m.msg_controllen = CMSG_SPACE(*n_fds * sizeof(int));
+
+ /* It's probably safe to assume that systems with SCM_RIGHTS also
+ know alloca() */
+ m.msg_control = alloca(m.msg_controllen);
+ memset(m.msg_control, 0, m.msg_controllen);
+
+ /* Do not include the padding at the end when we tell the kernel
+ * how much we're willing to receive. This avoids getting
+ * the padding filled with additional fds that we weren't expecting,
+ * if a (potentially malicious) sender included them. (fd.o #83622) */
+ m.msg_controllen = CMSG_LEN (*n_fds * sizeof(int));
+
+ again:
+
+ bytes_read = recvmsg (fd.fd, &m, 0
+#ifdef MSG_CMSG_CLOEXEC
+ |MSG_CMSG_CLOEXEC
+#endif
+ );
+
+ if (bytes_read < 0)
+ {
+ if (errno == EINTR)
+ goto again;
+ else
+ {
+ /* put length back (note that this doesn't actually realloc anything) */
+ _dbus_string_set_length (buffer, start);
+ return -1;
+ }
+ }
+ else
+ {
+ struct cmsghdr *cm;
+ dbus_bool_t found = FALSE;
+
+ for (cm = CMSG_FIRSTHDR(&m); cm; cm = CMSG_NXTHDR(&m, cm))
+ if (cm->cmsg_level == SOL_SOCKET && cm->cmsg_type == SCM_RIGHTS)
+ {
+ size_t i;
+ int *payload = (int *) (void *) CMSG_DATA (cm);
+ size_t payload_len_bytes = (cm->cmsg_len - CMSG_LEN (0));
+ size_t payload_len_fds;
+ size_t fds_to_use;
+
+ /* Every unsigned int fits in a size_t without truncation, so
+ * casting (size_t) *n_fds is OK */
+ _DBUS_STATIC_ASSERT (sizeof (size_t) >= sizeof (unsigned int));
+
+ if ((m.msg_flags & MSG_CTRUNC) && CMSG_NXTHDR(&m, cm) == NULL &&
+ (char *) payload + payload_len_bytes >
+ (char *) m.msg_control + m.msg_controllen)
+ {
+ /* This is the last cmsg in a truncated message and using
+ * cmsg_len would apparently overrun the allocated buffer.
+ * Some operating systems (illumos and Solaris are known) do
+ * not adjust cmsg_len in the last cmsg when truncation occurs.
+ * Adjust the payload length here. The calculation for
+ * payload_len_fds below will discard any trailing bytes that
+ * belong to an incomplete file descriptor - the kernel will
+ * have already closed that (at least for illumos and Solaris)
+ */
+ payload_len_bytes = m.msg_controllen -
+ ((char *) payload - (char *) m.msg_control);
+ }
+
+ payload_len_fds = payload_len_bytes / sizeof (int);
+
+ if (_DBUS_LIKELY (payload_len_fds <= (size_t) *n_fds))
+ {
+ /* The fds in the payload will fit in our buffer */
+ fds_to_use = payload_len_fds;
+ }
+ else
+ {
+ /* Too many fds in the payload. This shouldn't happen
+ * any more because we're setting m.msg_controllen to
+ * the exact number we can accept, but be safe and
+ * truncate. */
+ fds_to_use = (size_t) *n_fds;
+
+ /* Close the excess fds to avoid DoS: if they stayed open,
+ * someone could send us an extra fd per message
+ * and we'd eventually run out. */
+ for (i = fds_to_use; i < payload_len_fds; i++)
+ {
+ close (payload[i]);
+ }
+ }
+
+ memcpy (fds, payload, fds_to_use * sizeof (int));
+ found = TRUE;
+ /* This narrowing cast from size_t to unsigned int cannot
+ * overflow because we have chosen fds_to_use
+ * to be <= *n_fds */
+ *n_fds = (unsigned int) fds_to_use;
+
+ /* Linux doesn't tell us whether MSG_CMSG_CLOEXEC actually
+ worked, hence we need to go through this list and set
+ CLOEXEC everywhere in any case */
+ for (i = 0; i < fds_to_use; i++)
+ _dbus_fd_set_close_on_exec(fds[i]);
+
+ break;
+ }
+
+ if (!found)
+ *n_fds = 0;
+
+ if (m.msg_flags & MSG_CTRUNC)
+ {
+ unsigned int i;
+
+ /* Hmm, apparently the control data was truncated. The bad
+ thing is that we might have completely lost a couple of fds
+ without chance to recover them. Hence let's treat this as a
+ serious error. */
+
+ /* We still need to close whatever fds we *did* receive,
+ * otherwise they'll never get closed. (CVE-2020-12049) */
+ for (i = 0; i < *n_fds; i++)
+ close (fds[i]);
+
+ *n_fds = 0;
+ errno = ENOSPC;
+ _dbus_string_set_length (buffer, start);
+ return -1;
+ }
+
+ /* put length back (doesn't actually realloc) */
+ _dbus_string_set_length (buffer, start + bytes_read);
+
+#if 0
+ if (bytes_read > 0)
+ _dbus_verbose_bytes_of_string (buffer, start, bytes_read);
+#endif
+
+ return bytes_read;
+ }
+#endif
+}
+
+int
+_dbus_write_socket_with_unix_fds(DBusSocket fd,
+ const DBusString *buffer,
+ int start,
+ int len,
+ const int *fds,
+ int n_fds) {
+
+#ifndef HAVE_UNIX_FD_PASSING
+
+ if (n_fds > 0) {
+ errno = ENOTSUP;
+ return -1;
+ }
+
+ return _dbus_write_socket(fd, buffer, start, len);
+#else
+ return _dbus_write_socket_with_unix_fds_two(fd, buffer, start, len, NULL, 0, 0, fds, n_fds);
+#endif
+}
+
+int
+_dbus_write_socket_with_unix_fds_two(DBusSocket fd,
+ const DBusString *buffer1,
+ int start1,
+ int len1,
+ const DBusString *buffer2,
+ int start2,
+ int len2,
+ const int *fds,
+ int n_fds) {
+
+#ifndef HAVE_UNIX_FD_PASSING
+
+ if (n_fds > 0) {
+ errno = ENOTSUP;
+ return -1;
+ }
+
+ return _dbus_write_socket_two(fd,
+ buffer1, start1, len1,
+ buffer2, start2, len2);
+#else
+
+ struct msghdr m;
+ struct cmsghdr *cm;
+ struct iovec iov[2];
+ int bytes_written;
+
+ _dbus_assert (len1 >= 0);
+ _dbus_assert (len2 >= 0);
+ _dbus_assert (n_fds >= 0);
+
+ _DBUS_ZERO(iov);
+ iov[0].iov_base = (char*) _dbus_string_get_const_data_len (buffer1, start1, len1);
+ iov[0].iov_len = len1;
+
+ if (buffer2)
+ {
+ iov[1].iov_base = (char*) _dbus_string_get_const_data_len (buffer2, start2, len2);
+ iov[1].iov_len = len2;
+ }
+
+ _DBUS_ZERO(m);
+ m.msg_iov = iov;
+ m.msg_iovlen = buffer2 ? 2 : 1;
+
+ if (n_fds > 0)
+ {
+ m.msg_controllen = CMSG_SPACE(n_fds * sizeof(int));
+ m.msg_control = alloca(m.msg_controllen);
+ memset(m.msg_control, 0, m.msg_controllen);
+
+ cm = CMSG_FIRSTHDR(&m);
+ cm->cmsg_level = SOL_SOCKET;
+ cm->cmsg_type = SCM_RIGHTS;
+ cm->cmsg_len = CMSG_LEN(n_fds * sizeof(int));
+ memcpy(CMSG_DATA(cm), fds, n_fds * sizeof(int));
+ }
+
+ again:
+
+ bytes_written = sendmsg (fd.fd, &m, 0
+#if HAVE_DECL_MSG_NOSIGNAL
+ |MSG_NOSIGNAL
+#endif
+ );
+
+ if (bytes_written < 0 && errno == EINTR)
+ goto again;
+
+#if 0
+ if (bytes_written > 0)
+ _dbus_verbose_bytes_of_string (buffer, start, bytes_written);
+#endif
+
+ return bytes_written;
+#endif
+}
+
+/**
+ * Like _dbus_write_two() but only works on sockets and is thus
+ * available on Windows.
+ *
+ * @param fd the file descriptor
+ * @param buffer1 first buffer
+ * @param start1 first byte to write in first buffer
+ * @param len1 number of bytes to write from first buffer
+ * @param buffer2 second buffer, or #NULL
+ * @param start2 first byte to write in second buffer
+ * @param len2 number of bytes to write in second buffer
+ * @returns total bytes written from both buffers, or -1 on error
+ */
+int
+_dbus_write_socket_two (DBusSocket fd,
+ const DBusString *buffer1,
+ int start1,
+ int len1,
+ const DBusString *buffer2,
+ int start2,
+ int len2)
+{
+#if HAVE_DECL_MSG_NOSIGNAL
+ struct iovec vectors[2];
+ const char *data1;
+ const char *data2;
+ int bytes_written;
+ struct msghdr m;
+
+ _dbus_assert (buffer1 != NULL);
+ _dbus_assert (start1 >= 0);
+ _dbus_assert (start2 >= 0);
+ _dbus_assert (len1 >= 0);
+ _dbus_assert (len2 >= 0);
+
+ data1 = _dbus_string_get_const_data_len (buffer1, start1, len1);
+
+ if (buffer2 != NULL)
+ data2 = _dbus_string_get_const_data_len (buffer2, start2, len2);
+ else
+ {
+ data2 = NULL;
+ start2 = 0;
+ len2 = 0;
+ }
+
+ vectors[0].iov_base = (char*) data1;
+ vectors[0].iov_len = len1;
+ vectors[1].iov_base = (char*) data2;
+ vectors[1].iov_len = len2;
+
+ _DBUS_ZERO(m);
+ m.msg_iov = vectors;
+ m.msg_iovlen = data2 ? 2 : 1;
+
+ again:
+
+ bytes_written = sendmsg (fd.fd, &m, MSG_NOSIGNAL);
+
+ if (bytes_written < 0 && errno == EINTR)
+ goto again;
+
+ return bytes_written;
+
+#else
+ return _dbus_write_two (fd.fd, buffer1, start1, len1,
+ buffer2, start2, len2);
+#endif
+}
+
+/**
+ * Thin wrapper around the read() system call that appends
+ * the data it reads to the DBusString buffer. It appends
+ * up to the given count, and returns the same value
+ * and same errno as read(). The only exception is that
+ * _dbus_read() handles EINTR for you. Also, _dbus_read() can
+ * return ENOMEM, even though regular UNIX read doesn't.
+ *
+ * Unlike _dbus_read_socket(), _dbus_read() is not available
+ * on Windows.
+ *
+ * @param fd the file descriptor to read from
+ * @param buffer the buffer to append data to
+ * @param count the amount of data to read
+ * @returns the number of bytes read or -1
+ */
+int
+_dbus_read (int fd,
+ DBusString *buffer,
+ int count)
+{
+ int bytes_read;
+ int start;
+ char *data;
+
+ _dbus_assert (count >= 0);
+
+ start = _dbus_string_get_length (buffer);
+
+ if (!_dbus_string_lengthen (buffer, count))
+ {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ data = _dbus_string_get_data_len (buffer, start, count);
+
+ again:
+
+ bytes_read = read (fd, data, count);
+
+ if (bytes_read < 0)
+ {
+ if (errno == EINTR)
+ goto again;
+ else
+ {
+ /* put length back (note that this doesn't actually realloc anything) */
+ _dbus_string_set_length (buffer, start);
+ return -1;
+ }
+ }
+ else
+ {
+ /* put length back (doesn't actually realloc) */
+ _dbus_string_set_length (buffer, start + bytes_read);
+
+#if 0
+ if (bytes_read > 0)
+ _dbus_verbose_bytes_of_string (buffer, start, bytes_read);
+#endif
+
+ return bytes_read;
+ }
+}
+
+/**
+ * Thin wrapper around the write() system call that writes a part of a
+ * DBusString and handles EINTR for you.
+ *
+ * @param fd the file descriptor to write
+ * @param buffer the buffer to write data from
+ * @param start the first byte in the buffer to write
+ * @param len the number of bytes to try to write
+ * @returns the number of bytes written or -1 on error
+ */
+int
+_dbus_write (int fd,
+ const DBusString *buffer,
+ int start,
+ int len)
+{
+ const char *data;
+ int bytes_written;
+
+ data = _dbus_string_get_const_data_len (buffer, start, len);
+
+ again:
+
+ bytes_written = write (fd, data, len);
+
+ if (bytes_written < 0 && errno == EINTR)
+ goto again;
+
+#if 0
+ if (bytes_written > 0)
+ _dbus_verbose_bytes_of_string (buffer, start, bytes_written);
+#endif
+
+ return bytes_written;
+}
+
+/**
+ * Like _dbus_write() but will use writev() if possible
+ * to write both buffers in sequence. The return value
+ * is the number of bytes written in the first buffer,
+ * plus the number written in the second. If the first
+ * buffer is written successfully and an error occurs
+ * writing the second, the number of bytes in the first
+ * is returned (i.e. the error is ignored), on systems that
+ * don't have writev. Handles EINTR for you.
+ * The second buffer may be #NULL.
+ *
+ * @param fd the file descriptor
+ * @param buffer1 first buffer
+ * @param start1 first byte to write in first buffer
+ * @param len1 number of bytes to write from first buffer
+ * @param buffer2 second buffer, or #NULL
+ * @param start2 first byte to write in second buffer
+ * @param len2 number of bytes to write in second buffer
+ * @returns total bytes written from both buffers, or -1 on error
+ */
+int
+_dbus_write_two (int fd,
+ const DBusString *buffer1,
+ int start1,
+ int len1,
+ const DBusString *buffer2,
+ int start2,
+ int len2)
+{
+ _dbus_assert (buffer1 != NULL);
+ _dbus_assert (start1 >= 0);
+ _dbus_assert (start2 >= 0);
+ _dbus_assert (len1 >= 0);
+ _dbus_assert (len2 >= 0);
+
+#ifdef HAVE_WRITEV
+ {
+ struct iovec vectors[2];
+ const char *data1;
+ const char *data2;
+ int bytes_written;
+
+ data1 = _dbus_string_get_const_data_len (buffer1, start1, len1);
+
+ if (buffer2 != NULL)
+ data2 = _dbus_string_get_const_data_len (buffer2, start2, len2);
+ else
+ {
+ data2 = NULL;
+ start2 = 0;
+ len2 = 0;
+ }
+
+ vectors[0].iov_base = (char*) data1;
+ vectors[0].iov_len = len1;
+ vectors[1].iov_base = (char*) data2;
+ vectors[1].iov_len = len2;
+
+ again:
+
+ bytes_written = writev (fd,
+ vectors,
+ data2 ? 2 : 1);
+
+ if (bytes_written < 0 && errno == EINTR)
+ goto again;
+
+ return bytes_written;
+ }
+#else /* HAVE_WRITEV */
+ {
+ int ret1, ret2;
+
+ ret1 = _dbus_write (fd, buffer1, start1, len1);
+ if (ret1 == len1 && buffer2 != NULL)
+ {
+ ret2 = _dbus_write (fd, buffer2, start2, len2);
+ if (ret2 < 0)
+ ret2 = 0; /* we can't report an error as the first write was OK */
+
+ return ret1 + ret2;
+ }
+ else
+ return ret1;
+ }
+#endif /* !HAVE_WRITEV */
+}
+
+/**
+ * Creates a socket and connects it to the UNIX domain socket at the
+ * given path. The connection fd is returned, and is set up as
+ * nonblocking.
+ *
+ * Uses abstract sockets instead of filesystem-linked sockets if
+ * requested (it's possible only on Linux; see "man 7 unix" on Linux).
+ * On non-Linux abstract socket usage always fails.
+ *
+ * This will set FD_CLOEXEC for the socket returned.
+ *
+ * @param path the path to UNIX domain socket
+ * @param abstract #TRUE to use abstract namespace
+ * @param error return location for error code
+ * @returns a valid socket on success or an invalid socket on error
+ */
+DBusSocket
+_dbus_connect_unix_socket (const char *path,
+ dbus_bool_t abstract,
+ DBusError *error)
+{
+ DBusSocket fd = DBUS_SOCKET_INIT;
+ size_t path_len;
+ struct sockaddr_un addr;
+ _DBUS_STATIC_ASSERT (sizeof (addr.sun_path) > _DBUS_MAX_SUN_PATH_LENGTH);
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ _dbus_verbose ("connecting to unix socket %s abstract=%d\n",
+ path, abstract);
+
+
+ if (!_dbus_open_unix_socket (&fd.fd, error))
+ {
+ _DBUS_ASSERT_ERROR_IS_SET(error);
+ return fd;
+ }
+ _DBUS_ASSERT_ERROR_IS_CLEAR(error);
+
+ _DBUS_ZERO (addr);
+ addr.sun_family = AF_UNIX;
+ path_len = strlen (path);
+
+ if (abstract)
+ {
+#ifdef __linux__
+ addr.sun_path[0] = '\0'; /* this is what says "use abstract" */
+ path_len++; /* Account for the extra nul byte added to the start of sun_path */
+
+ if (path_len > _DBUS_MAX_SUN_PATH_LENGTH)
+ {
+ dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS,
+ "Abstract socket name too long\n");
+ _dbus_close_socket (&fd, NULL);
+ return fd;
+ }
+
+ strncpy (&addr.sun_path[1], path, sizeof (addr.sun_path) - 2);
+ /* _dbus_verbose_bytes (addr.sun_path, sizeof (addr.sun_path)); */
+#else /* !__linux__ */
+ dbus_set_error (error, DBUS_ERROR_NOT_SUPPORTED,
+ "Operating system does not support abstract socket namespace\n");
+ _dbus_close_socket (&fd, NULL);
+ return fd;
+#endif /* !__linux__ */
+ }
+ else
+ {
+ if (path_len > _DBUS_MAX_SUN_PATH_LENGTH)
+ {
+ dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS,
+ "Socket name too long\n");
+ _dbus_close_socket (&fd, NULL);
+ return fd;
+ }
+
+ strncpy (addr.sun_path, path, sizeof (addr.sun_path) - 1);
+ }
+
+ if (connect (fd.fd, (struct sockaddr*) &addr, _DBUS_STRUCT_OFFSET (struct sockaddr_un, sun_path) + path_len) < 0)
+ {
+ dbus_set_error (error,
+ _dbus_error_from_errno (errno),
+ "Failed to connect to socket %s: %s",
+ path, _dbus_strerror (errno));
+
+ _dbus_close_socket (&fd, NULL);
+ return fd;
+ }
+
+ if (!_dbus_set_fd_nonblocking (fd.fd, error))
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+
+ _dbus_close_socket (&fd, NULL);
+ return fd;
+ }
+
+ return fd;
+}
+
+/**
+ * Creates a UNIX domain socket and connects it to the specified
+ * process to execute.
+ *
+ * This will set FD_CLOEXEC for the socket returned.
+ *
+ * @param path the path to the executable
+ * @param argv the argument list for the process to execute.
+ * argv[0] typically is identical to the path of the executable
+ * @param error return location for error code
+ * @returns a valid socket on success or an invalid socket on error
+ */
+DBusSocket
+_dbus_connect_exec (const char *path,
+ char *const argv[],
+ DBusError *error)
+{
+ DBusSocket s = DBUS_SOCKET_INIT;
+ int fds[2];
+ pid_t pid;
+ int retval;
+ dbus_bool_t cloexec_done = 0;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ _dbus_verbose ("connecting to process %s\n", path);
+
+#ifdef SOCK_CLOEXEC
+ retval = socketpair (AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0, fds);
+ cloexec_done = (retval >= 0);
+
+ if (retval < 0 && (errno == EINVAL || errno == EPROTOTYPE))
+#endif
+ {
+ retval = socketpair (AF_UNIX, SOCK_STREAM, 0, fds);
+ }
+
+ if (retval < 0)
+ {
+ dbus_set_error (error,
+ _dbus_error_from_errno (errno),
+ "Failed to create socket pair: %s",
+ _dbus_strerror (errno));
+ _dbus_assert (!_dbus_socket_is_valid (s));
+ return s;
+ }
+
+ if (!cloexec_done)
+ {
+ _dbus_fd_set_close_on_exec (fds[0]);
+ _dbus_fd_set_close_on_exec (fds[1]);
+ }
+
+ /* Make sure our output buffers aren't redundantly printed by both the
+ * parent and the child */
+ fflush (stdout);
+ fflush (stderr);
+
+ pid = fork ();
+ if (pid < 0)
+ {
+ dbus_set_error (error,
+ _dbus_error_from_errno (errno),
+ "Failed to fork() to call %s: %s",
+ path, _dbus_strerror (errno));
+ close (fds[0]);
+ close (fds[1]);
+ _dbus_assert (!_dbus_socket_is_valid (s));
+ return s;
+ }
+
+ if (pid == 0)
+ {
+ /* child */
+ close (fds[0]);
+
+ dup2 (fds[1], STDIN_FILENO);
+ dup2 (fds[1], STDOUT_FILENO);
+
+ if (fds[1] != STDIN_FILENO &&
+ fds[1] != STDOUT_FILENO)
+ close (fds[1]);
+
+ /* Inherit STDERR and the controlling terminal from the
+ parent */
+
+ _dbus_close_all ();
+
+ execvp (path, (char * const *) argv);
+
+ fprintf (stderr, "Failed to execute process %s: %s\n", path, _dbus_strerror (errno));
+
+ _exit(1);
+ }
+
+ /* parent */
+ close (fds[1]);
+
+ if (!_dbus_set_fd_nonblocking (fds[0], error))
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+
+ close (fds[0]);
+ _dbus_assert (!_dbus_socket_is_valid (s));
+ return s;
+ }
+
+ s.fd = fds[0];
+ return s;
+}
+
+/**
+ * Creates a socket and binds it to the given path,
+ * then listens on the socket. The socket is
+ * set to be nonblocking.
+ *
+ * Uses abstract sockets instead of filesystem-linked
+ * sockets if requested (it's possible only on Linux;
+ * see "man 7 unix" on Linux).
+ * On non-Linux abstract socket usage always fails.
+ *
+ * This will set FD_CLOEXEC for the socket returned
+ *
+ * @param path the socket name
+ * @param abstract #TRUE to use abstract namespace
+ * @param error return location for errors
+ * @returns a valid socket on success or an invalid socket on error
+ */
+DBusSocket
+_dbus_listen_unix_socket (const char *path,
+ dbus_bool_t abstract,
+ DBusError *error)
+{
+ DBusSocket s = DBUS_SOCKET_INIT;
+ int listen_fd;
+ struct sockaddr_un addr;
+ size_t path_len;
+ _DBUS_STATIC_ASSERT (sizeof (addr.sun_path) > _DBUS_MAX_SUN_PATH_LENGTH);
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ _dbus_verbose ("listening on unix socket %s abstract=%d\n",
+ path, abstract);
+
+ if (!_dbus_open_unix_socket (&listen_fd, error))
+ {
+ _DBUS_ASSERT_ERROR_IS_SET(error);
+ return s;
+ }
+ _DBUS_ASSERT_ERROR_IS_CLEAR(error);
+
+ _DBUS_ZERO (addr);
+ addr.sun_family = AF_UNIX;
+ path_len = strlen (path);
+
+ if (abstract)
+ {
+#ifdef __linux__
+ /* remember that abstract names aren't nul-terminated so we rely
+ * on sun_path being filled in with zeroes above.
+ */
+ addr.sun_path[0] = '\0'; /* this is what says "use abstract" */
+ path_len++; /* Account for the extra nul byte added to the start of sun_path */
+
+ if (path_len > _DBUS_MAX_SUN_PATH_LENGTH)
+ {
+ dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS,
+ "Abstract socket name too long\n");
+ _dbus_close (listen_fd, NULL);
+ return s;
+ }
+
+ strncpy (&addr.sun_path[1], path, sizeof (addr.sun_path) - 2);
+ /* _dbus_verbose_bytes (addr.sun_path, sizeof (addr.sun_path)); */
+#else /* !__linux__ */
+ dbus_set_error (error, DBUS_ERROR_NOT_SUPPORTED,
+ "Operating system does not support abstract socket namespace\n");
+ _dbus_close (listen_fd, NULL);
+ return s;
+#endif /* !__linux__ */
+ }
+ else
+ {
+ /* Discussed security implications of this with Nalin,
+ * and we couldn't think of where it would kick our ass, but
+ * it still seems a bit sucky. It also has non-security suckage;
+ * really we'd prefer to exit if the socket is already in use.
+ * But there doesn't seem to be a good way to do this.
+ *
+ * Just to be extra careful, I threw in the stat() - clearly
+ * the stat() can't *fix* any security issue, but it at least
+ * avoids inadvertent/accidental data loss.
+ */
+ {
+ struct stat sb;
+
+ if (stat (path, &sb) == 0 &&
+ S_ISSOCK (sb.st_mode))
+ unlink (path);
+ }
+
+ if (path_len > _DBUS_MAX_SUN_PATH_LENGTH)
+ {
+ dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS,
+ "Socket name too long\n");
+ _dbus_close (listen_fd, NULL);
+ return s;
+ }
+
+ strncpy (addr.sun_path, path, sizeof (addr.sun_path) - 1);
+ }
+
+ if (bind (listen_fd, (struct sockaddr*) &addr, _DBUS_STRUCT_OFFSET (struct sockaddr_un, sun_path) + path_len) < 0)
+ {
+ dbus_set_error (error, _dbus_error_from_errno (errno),
+ "Failed to bind socket \"%s\": %s",
+ path, _dbus_strerror (errno));
+ _dbus_close (listen_fd, NULL);
+ return s;
+ }
+
+ if (listen (listen_fd, SOMAXCONN /* backlog */) < 0)
+ {
+ dbus_set_error (error, _dbus_error_from_errno (errno),
+ "Failed to listen on socket \"%s\": %s",
+ path, _dbus_strerror (errno));
+ _dbus_close (listen_fd, NULL);
+ return s;
+ }
+
+ if (!_dbus_set_fd_nonblocking (listen_fd, error))
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ _dbus_close (listen_fd, NULL);
+ return s;
+ }
+
+ /* Try opening up the permissions, but if we can't, just go ahead
+ * and continue, maybe it will be good enough.
+ */
+ if (!abstract && chmod (path, 0777) < 0)
+ _dbus_warn ("Could not set mode 0777 on socket %s", path);
+
+ s.fd = listen_fd;
+ return s;
+}
+
+/**
+ * Acquires one or more sockets passed in from systemd. The sockets
+ * are set to be nonblocking.
+ *
+ * This will set FD_CLOEXEC for the sockets returned.
+ *
+ * @param fds the file descriptors
+ * @param error return location for errors
+ * @returns the number of file descriptors
+ */
+int
+_dbus_listen_systemd_sockets (DBusSocket **fds,
+ DBusError *error)
+{
+#ifdef HAVE_SYSTEMD
+ int r, n;
+ int fd;
+ DBusSocket *new_fds;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ n = sd_listen_fds (TRUE);
+ if (n < 0)
+ {
+ dbus_set_error (error, _dbus_error_from_errno (-n),
+ "Failed to acquire systemd socket: %s",
+ _dbus_strerror (-n));
+ return -1;
+ }
+
+ if (n <= 0)
+ {
+ dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS,
+ "No socket received.");
+ return -1;
+ }
+
+ for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd ++)
+ {
+ r = sd_is_socket (fd, AF_UNSPEC, SOCK_STREAM, 1);
+ if (r < 0)
+ {
+ dbus_set_error (error, _dbus_error_from_errno (-r),
+ "Failed to verify systemd socket type: %s",
+ _dbus_strerror (-r));
+ return -1;
+ }
+
+ if (!r)
+ {
+ dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS,
+ "Passed socket has wrong type.");
+ return -1;
+ }
+ }
+
+ /* OK, the file descriptors are all good, so let's take posession of
+ them then. */
+
+ new_fds = dbus_new (DBusSocket, n);
+ if (!new_fds)
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY,
+ "Failed to allocate file handle array.");
+ goto fail;
+ }
+
+ for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd ++)
+ {
+ if (!_dbus_set_fd_nonblocking (fd, error))
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ goto fail;
+ }
+
+ new_fds[fd - SD_LISTEN_FDS_START].fd = fd;
+ }
+
+ *fds = new_fds;
+ return n;
+
+ fail:
+
+ for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd ++)
+ {
+ _dbus_close (fd, NULL);
+ }
+
+ dbus_free (new_fds);
+ return -1;
+#else
+ dbus_set_error_const (error, DBUS_ERROR_NOT_SUPPORTED,
+ "dbus was compiled without systemd support");
+ return -1;
+#endif
+}
+
+/* Convert an error code from getaddrinfo() or getnameinfo() into
+ * a D-Bus error name. */
+static const char *
+_dbus_error_from_gai (int gai_res,
+ int saved_errno)
+{
+ switch (gai_res)
+ {
+#ifdef EAI_FAMILY
+ case EAI_FAMILY:
+ /* ai_family not supported (at all) */
+ return DBUS_ERROR_NOT_SUPPORTED;
+#endif
+
+#ifdef EAI_SOCKTYPE
+ case EAI_SOCKTYPE:
+ /* ai_socktype not supported (at all) */
+ return DBUS_ERROR_NOT_SUPPORTED;
+#endif
+
+#ifdef EAI_MEMORY
+ case EAI_MEMORY:
+ /* Out of memory */
+ return DBUS_ERROR_NO_MEMORY;
+#endif
+
+#ifdef EAI_SYSTEM
+ case EAI_SYSTEM:
+ /* Unspecified system error, details in errno */
+ return _dbus_error_from_errno (saved_errno);
+#endif
+
+ case 0:
+ /* It succeeded, but we didn't get any addresses? */
+ return DBUS_ERROR_FAILED;
+
+ /* EAI_AGAIN: Transient failure */
+ /* EAI_BADFLAGS: invalid ai_flags (programming error) */
+ /* EAI_FAIL: Non-recoverable failure */
+ /* EAI_NODATA: host exists but has no addresses */
+ /* EAI_NONAME: host does not exist */
+ /* EAI_OVERFLOW: argument buffer overflow */
+ /* EAI_SERVICE: service not available for specified socket
+ * type (we should never see this because we use numeric
+ * ports) */
+ default:
+ return DBUS_ERROR_FAILED;
+ }
+}
+
+/**
+ * Creates a socket and connects to a socket at the given host
+ * and port. The connection fd is returned, and is set up as
+ * nonblocking.
+ *
+ * This will set FD_CLOEXEC for the socket returned
+ *
+ * @param host the host name to connect to
+ * @param port the port to connect to
+ * @param family the address family to listen on, NULL for all
+ * @param error return location for error code
+ * @returns connection file descriptor or -1 on error
+ */
+DBusSocket
+_dbus_connect_tcp_socket (const char *host,
+ const char *port,
+ const char *family,
+ DBusError *error)
+{
+ return _dbus_connect_tcp_socket_with_nonce (host, port, family, (const char*)NULL, error);
+}
+
+DBusSocket
+_dbus_connect_tcp_socket_with_nonce (const char *host,
+ const char *port,
+ const char *family,
+ const char *noncefile,
+ DBusError *error)
+{
+ int saved_errno = 0;
+ DBusList *connect_errors = NULL;
+ DBusSocket fd = DBUS_SOCKET_INIT;
+ int res;
+ struct addrinfo hints;
+ struct addrinfo *ai = NULL;
+ const struct addrinfo *tmp;
+ DBusError *connect_error;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR(error);
+
+ _DBUS_ZERO (hints);
+
+ if (!family)
+ hints.ai_family = AF_UNSPEC;
+ else if (!strcmp(family, "ipv4"))
+ hints.ai_family = AF_INET;
+ else if (!strcmp(family, "ipv6"))
+ hints.ai_family = AF_INET6;
+ else
+ {
+ dbus_set_error (error,
+ DBUS_ERROR_BAD_ADDRESS,
+ "Unknown address family %s", family);
+ return _dbus_socket_get_invalid ();
+ }
+ hints.ai_protocol = IPPROTO_TCP;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_ADDRCONFIG;
+
+ if ((res = getaddrinfo(host, port, &hints, &ai)) != 0)
+ {
+ dbus_set_error (error,
+ _dbus_error_from_gai (res, errno),
+ "Failed to lookup host/port: \"%s:%s\": %s (%d)",
+ host, port, gai_strerror(res), res);
+ goto out;
+ }
+
+ tmp = ai;
+ while (tmp)
+ {
+ if (!_dbus_open_socket (&fd.fd, tmp->ai_family, SOCK_STREAM, 0, error))
+ {
+ _DBUS_ASSERT_ERROR_IS_SET(error);
+ _dbus_socket_invalidate (&fd);
+ goto out;
+ }
+ _DBUS_ASSERT_ERROR_IS_CLEAR(error);
+
+ if (connect (fd.fd, (struct sockaddr*) tmp->ai_addr, tmp->ai_addrlen) < 0)
+ {
+ saved_errno = errno;
+ _dbus_close_socket (&fd, NULL);
+
+ connect_error = dbus_new0 (DBusError, 1);
+
+ if (connect_error == NULL)
+ {
+ _DBUS_SET_OOM (error);
+ goto out;
+ }
+
+ dbus_error_init (connect_error);
+ _dbus_set_error_with_inet_sockaddr (connect_error,
+ tmp->ai_addr, tmp->ai_addrlen,
+ "Failed to connect to socket",
+ saved_errno);
+
+ if (!_dbus_list_append (&connect_errors, connect_error))
+ {
+ dbus_error_free (connect_error);
+ dbus_free (connect_error);
+ _DBUS_SET_OOM (error);
+ goto out;
+ }
+
+ tmp = tmp->ai_next;
+ continue;
+ }
+
+ break;
+ }
+
+ if (!_dbus_socket_is_valid (fd))
+ {
+ _dbus_combine_tcp_errors (&connect_errors, "Failed to connect",
+ host, port, error);
+ goto out;
+ }
+
+ if (noncefile != NULL)
+ {
+ DBusString noncefileStr;
+ dbus_bool_t ret;
+ _dbus_string_init_const (&noncefileStr, noncefile);
+ ret = _dbus_send_nonce (fd, &noncefileStr, error);
+
+ if (!ret)
+ {
+ _dbus_close_socket (&fd, NULL);
+ goto out;
+ }
+ }
+
+ if (!_dbus_set_fd_nonblocking (fd.fd, error))
+ {
+ _dbus_close_socket (&fd, NULL);
+ goto out;
+ }
+
+out:
+ if (ai != NULL)
+ freeaddrinfo (ai);
+
+ while ((connect_error = _dbus_list_pop_first (&connect_errors)))
+ {
+ dbus_error_free (connect_error);
+ dbus_free (connect_error);
+ }
+
+ return fd;
+}
+
+/**
+ * Creates a socket and binds it to the given path, then listens on
+ * the socket. The socket is set to be nonblocking. In case of port=0
+ * a random free port is used and returned in the port parameter.
+ * If inaddr_any is specified, the hostname is ignored.
+ *
+ * This will set FD_CLOEXEC for the socket returned
+ *
+ * @param host the host name to listen on
+ * @param port the port to listen on, if zero a free port will be used
+ * @param family the address family to listen on, NULL for all
+ * @param retport string to return the actual port listened on
+ * @param retfamily string to return the actual family listened on
+ * @param fds_p location to store returned file descriptors
+ * @param error return location for errors
+ * @returns the number of listening file descriptors or -1 on error
+ */
+int
+_dbus_listen_tcp_socket (const char *host,
+ const char *port,
+ const char *family,
+ DBusString *retport,
+ const char **retfamily,
+ DBusSocket **fds_p,
+ DBusError *error)
+{
+ int saved_errno;
+ int nlisten_fd = 0, res, i;
+ DBusList *bind_errors = NULL;
+ DBusError *bind_error = NULL;
+ DBusSocket *listen_fd = NULL;
+ struct addrinfo hints;
+ struct addrinfo *ai, *tmp;
+ unsigned int reuseaddr;
+ dbus_bool_t have_ipv4 = FALSE;
+ dbus_bool_t have_ipv6 = FALSE;
+
+ *fds_p = NULL;
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ _DBUS_ZERO (hints);
+
+ if (!family)
+ hints.ai_family = AF_UNSPEC;
+ else if (!strcmp(family, "ipv4"))
+ hints.ai_family = AF_INET;
+ else if (!strcmp(family, "ipv6"))
+ hints.ai_family = AF_INET6;
+ else
+ {
+ dbus_set_error (error,
+ DBUS_ERROR_BAD_ADDRESS,
+ "Unknown address family %s", family);
+ return -1;
+ }
+
+ hints.ai_protocol = IPPROTO_TCP;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_ADDRCONFIG | AI_PASSIVE;
+
+ redo_lookup_with_port:
+ ai = NULL;
+ if ((res = getaddrinfo(host, port, &hints, &ai)) != 0 || !ai)
+ {
+ dbus_set_error (error,
+ _dbus_error_from_gai (res, errno),
+ "Failed to lookup host/port: \"%s:%s\": %s (%d)",
+ host ? host : "*", port, gai_strerror(res), res);
+ goto failed;
+ }
+
+ tmp = ai;
+ while (tmp)
+ {
+ int fd = -1, tcp_nodelay_on;
+ DBusSocket *newlisten_fd;
+
+ if (!_dbus_open_socket (&fd, tmp->ai_family, SOCK_STREAM, 0, error))
+ {
+ _DBUS_ASSERT_ERROR_IS_SET(error);
+ goto failed;
+ }
+ _DBUS_ASSERT_ERROR_IS_CLEAR(error);
+
+ reuseaddr = 1;
+ if (setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(reuseaddr))==-1)
+ {
+ _dbus_warn ("Failed to set socket option \"%s:%s\": %s",
+ host ? host : "*", port, _dbus_strerror (errno));
+ }
+
+ /* Nagle's algorithm imposes a huge delay on the initial messages
+ going over TCP. */
+ tcp_nodelay_on = 1;
+ if (setsockopt (fd, IPPROTO_TCP, TCP_NODELAY, &tcp_nodelay_on, sizeof (tcp_nodelay_on)) == -1)
+ {
+ _dbus_warn ("Failed to set TCP_NODELAY socket option \"%s:%s\": %s",
+ host ? host : "*", port, _dbus_strerror (errno));
+ }
+
+ if (bind (fd, (struct sockaddr*) tmp->ai_addr, tmp->ai_addrlen) < 0)
+ {
+ saved_errno = errno;
+ _dbus_close(fd, NULL);
+
+ /*
+ * We don't treat this as a fatal error, because there might be
+ * other addresses that we can listen on. In particular:
+ *
+ * - If saved_errno is EADDRINUSE after we
+ * "goto redo_lookup_with_port" after binding a port on one of the
+ * possible addresses, we will try to bind that same port on
+ * every address, including the same address again for a second
+ * time, which will fail with EADDRINUSE.
+ *
+ * - If saved_errno is EADDRINUSE, it might be because binding to
+ * an IPv6 address implicitly binds to a corresponding IPv4
+ * address or vice versa (e.g. Linux with bindv6only=0).
+ *
+ * - If saved_errno is EADDRNOTAVAIL when we asked for family
+ * AF_UNSPEC, it might be because IPv6 is disabled for this
+ * particular interface (e.g. Linux with
+ * /proc/sys/net/ipv6/conf/lo/disable_ipv6).
+ */
+ bind_error = dbus_new0 (DBusError, 1);
+
+ if (bind_error == NULL)
+ {
+ _DBUS_SET_OOM (error);
+ goto failed;
+ }
+
+ dbus_error_init (bind_error);
+ _dbus_set_error_with_inet_sockaddr (bind_error, tmp->ai_addr, tmp->ai_addrlen,
+ "Failed to bind socket",
+ saved_errno);
+
+ if (!_dbus_list_append (&bind_errors, bind_error))
+ {
+ dbus_error_free (bind_error);
+ dbus_free (bind_error);
+ _DBUS_SET_OOM (error);
+ goto failed;
+ }
+
+ /* Try the next address, maybe it will work better */
+ tmp = tmp->ai_next;
+ continue;
+ }
+
+ if (listen (fd, 30 /* backlog */) < 0)
+ {
+ saved_errno = errno;
+ _dbus_close (fd, NULL);
+ _dbus_set_error_with_inet_sockaddr (error, tmp->ai_addr, tmp->ai_addrlen,
+ "Failed to listen on socket",
+ saved_errno);
+ goto failed;
+ }
+
+ newlisten_fd = dbus_realloc(listen_fd, sizeof(DBusSocket)*(nlisten_fd+1));
+ if (!newlisten_fd)
+ {
+ _dbus_close (fd, NULL);
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY,
+ "Failed to allocate file handle array");
+ goto failed;
+ }
+ listen_fd = newlisten_fd;
+ listen_fd[nlisten_fd].fd = fd;
+ nlisten_fd++;
+
+ if (tmp->ai_addr->sa_family == AF_INET)
+ have_ipv4 = TRUE;
+ else if (tmp->ai_addr->sa_family == AF_INET6)
+ have_ipv6 = TRUE;
+
+ if (!_dbus_string_get_length(retport))
+ {
+ /* If the user didn't specify a port, or used 0, then
+ the kernel chooses a port. After the first address
+ is bound to, we need to force all remaining addresses
+ to use the same port */
+ if (!port || !strcmp(port, "0"))
+ {
+ int result;
+ struct sockaddr_storage addr;
+ socklen_t addrlen;
+ char portbuf[50];
+
+ addrlen = sizeof(addr);
+ result = getsockname(fd, (struct sockaddr*) &addr, &addrlen);
+
+ if (result == -1)
+ {
+ saved_errno = errno;
+ dbus_set_error (error, _dbus_error_from_errno (saved_errno),
+ "Failed to retrieve socket name for \"%s:%s\": %s",
+ host ? host : "*", port, _dbus_strerror (saved_errno));
+ goto failed;
+ }
+
+ if ((res = getnameinfo ((struct sockaddr*)&addr, addrlen, NULL, 0,
+ portbuf, sizeof(portbuf),
+ NI_NUMERICHOST | NI_NUMERICSERV)) != 0)
+ {
+ saved_errno = errno;
+ dbus_set_error (error, _dbus_error_from_gai (res, saved_errno),
+ "Failed to resolve port \"%s:%s\": %s (%d)",
+ host ? host : "*", port, gai_strerror(res), res);
+ goto failed;
+ }
+
+ if (!_dbus_string_append(retport, portbuf))
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ goto failed;
+ }
+
+ /* Release current address list & redo lookup */
+ port = _dbus_string_get_const_data(retport);
+ freeaddrinfo(ai);
+ goto redo_lookup_with_port;
+ }
+ else
+ {
+ if (!_dbus_string_append(retport, port))
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ goto failed;
+ }
+ }
+ }
+
+ tmp = tmp->ai_next;
+ }
+ freeaddrinfo(ai);
+ ai = NULL;
+
+ if (!nlisten_fd)
+ {
+ _dbus_combine_tcp_errors (&bind_errors, "Failed to bind", host,
+ port, error);
+ goto failed;
+ }
+
+ if (have_ipv4 && !have_ipv6)
+ *retfamily = "ipv4";
+ else if (!have_ipv4 && have_ipv6)
+ *retfamily = "ipv6";
+
+ for (i = 0 ; i < nlisten_fd ; i++)
+ {
+ if (!_dbus_set_fd_nonblocking (listen_fd[i].fd, error))
+ {
+ goto failed;
+ }
+ }
+
+ *fds_p = listen_fd;
+
+ /* This list might be non-empty even on success, because we might be
+ * ignoring EADDRINUSE or EADDRNOTAVAIL */
+ while ((bind_error = _dbus_list_pop_first (&bind_errors)))
+ {
+ dbus_error_free (bind_error);
+ dbus_free (bind_error);
+ }
+
+ return nlisten_fd;
+
+ failed:
+ if (ai)
+ freeaddrinfo(ai);
+ for (i = 0 ; i < nlisten_fd ; i++)
+ _dbus_close(listen_fd[i].fd, NULL);
+
+ while ((bind_error = _dbus_list_pop_first (&bind_errors)))
+ {
+ dbus_error_free (bind_error);
+ dbus_free (bind_error);
+ }
+
+ dbus_free(listen_fd);
+ return -1;
+}
+
+static dbus_bool_t
+write_credentials_byte (int server_fd,
+ DBusError *error)
+{
+ int bytes_written;
+ char buf[1] = { '\0' };
+#if defined(HAVE_CMSGCRED)
+ union {
+ struct cmsghdr hdr;
+ char cred[CMSG_SPACE (sizeof (struct cmsgcred))];
+ } cmsg;
+ struct iovec iov;
+ struct msghdr msg;
+ iov.iov_base = buf;
+ iov.iov_len = 1;
+
+ _DBUS_ZERO(msg);
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ msg.msg_control = (caddr_t) &cmsg;
+ msg.msg_controllen = CMSG_SPACE (sizeof (struct cmsgcred));
+ _DBUS_ZERO(cmsg);
+ cmsg.hdr.cmsg_len = CMSG_LEN (sizeof (struct cmsgcred));
+ cmsg.hdr.cmsg_level = SOL_SOCKET;
+ cmsg.hdr.cmsg_type = SCM_CREDS;
+#endif
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ again:
+
+#if defined(HAVE_CMSGCRED)
+ bytes_written = sendmsg (server_fd, &msg, 0
+#if HAVE_DECL_MSG_NOSIGNAL
+ |MSG_NOSIGNAL
+#endif
+ );
+
+ /* If we HAVE_CMSGCRED, the OS still might not let us sendmsg()
+ * with a SOL_SOCKET/SCM_CREDS message - for instance, FreeBSD
+ * only allows that on AF_UNIX. Try just doing a send() instead. */
+ if (bytes_written < 0 && errno == EINVAL)
+#endif
+ {
+ bytes_written = send (server_fd, buf, 1, 0
+#if HAVE_DECL_MSG_NOSIGNAL
+ |MSG_NOSIGNAL
+#endif
+ );
+ }
+
+ if (bytes_written < 0 && errno == EINTR)
+ goto again;
+
+ if (bytes_written < 0)
+ {
+ dbus_set_error (error, _dbus_error_from_errno (errno),
+ "Failed to write credentials byte: %s",
+ _dbus_strerror (errno));
+ return FALSE;
+ }
+ else if (bytes_written == 0)
+ {
+ dbus_set_error (error, DBUS_ERROR_IO_ERROR,
+ "wrote zero bytes writing credentials byte");
+ return FALSE;
+ }
+ else
+ {
+ _dbus_assert (bytes_written == 1);
+ _dbus_verbose ("wrote credentials byte\n");
+ return TRUE;
+ }
+}
+
+/* return FALSE on OOM, TRUE otherwise, even if no groups were found */
+static dbus_bool_t
+add_groups_to_credentials (int client_fd,
+ DBusCredentials *credentials,
+ dbus_gid_t primary)
+{
+#if defined(__linux__) && defined(SO_PEERGROUPS)
+ _DBUS_STATIC_ASSERT (sizeof (gid_t) <= sizeof (dbus_gid_t));
+ /* This function assumes socklen_t is unsigned, which is true on Linux */
+ _DBUS_STATIC_ASSERT (((socklen_t) -1) > 0);
+ gid_t *buf = NULL;
+ socklen_t len = 1024;
+ dbus_bool_t oom = FALSE;
+ /* libdbus has a different representation of group IDs just to annoy you */
+ dbus_gid_t *converted_gids = NULL;
+ dbus_bool_t need_primary = TRUE;
+ size_t n_gids;
+ size_t i;
+
+ n_gids = ((size_t) len) / sizeof (gid_t);
+ buf = dbus_new (gid_t, n_gids);
+
+ if (buf == NULL)
+ return FALSE;
+
+ while (getsockopt (client_fd, SOL_SOCKET, SO_PEERGROUPS, buf, &len) < 0)
+ {
+ int e = errno;
+ gid_t *replacement;
+
+ _dbus_verbose ("getsockopt failed with %s, len now %lu\n",
+ _dbus_strerror (e), (unsigned long) len);
+
+ if (e != ERANGE || (size_t) len <= n_gids * sizeof (gid_t))
+ {
+ _dbus_verbose ("Failed to getsockopt(SO_PEERGROUPS): %s\n",
+ _dbus_strerror (e));
+ goto out;
+ }
+
+ /* If not enough space, len is updated to be enough.
+ * Try again with a large enough buffer. */
+ n_gids = ((size_t) len) / sizeof (gid_t);
+ replacement = dbus_realloc (buf, len);
+
+ if (replacement == NULL)
+ {
+ oom = TRUE;
+ goto out;
+ }
+
+ buf = replacement;
+ _dbus_verbose ("will try again with %lu\n", (unsigned long) len);
+ }
+
+ if (len > n_gids * sizeof (gid_t))
+ {
+ _dbus_verbose ("%lu > %zu", (unsigned long) len, n_gids * sizeof (gid_t));
+ _dbus_assert_not_reached ("getsockopt(SO_PEERGROUPS) overflowed");
+ }
+
+ if (len % sizeof (gid_t) != 0)
+ {
+ _dbus_verbose ("getsockopt(SO_PEERGROUPS) did not return an "
+ "integer multiple of sizeof(gid_t): %lu should be "
+ "divisible by %zu",
+ (unsigned long) len, sizeof (gid_t));
+ goto out;
+ }
+
+ /* Allocate an extra space for the primary group ID */
+ n_gids = ((size_t) len) / sizeof (gid_t);
+
+ /* If n_gids is less than this, then (n_gids + 1) certainly doesn't
+ * overflow, and neither does multiplying that by sizeof(dbus_gid_t).
+ * This is using _DBUS_INT32_MAX as a conservative lower bound for
+ * the maximum size_t. */
+ if (n_gids >= (_DBUS_INT32_MAX / sizeof (dbus_gid_t)) - 1)
+ {
+ _dbus_verbose ("getsockopt(SO_PEERGROUPS) returned a huge number "
+ "of groups (%lu bytes), ignoring",
+ (unsigned long) len);
+ goto out;
+ }
+
+ converted_gids = dbus_new (dbus_gid_t, n_gids + 1);
+
+ if (converted_gids == NULL)
+ {
+ oom = TRUE;
+ goto out;
+ }
+
+ for (i = 0; i < n_gids; i++)
+ {
+ converted_gids[i] = (dbus_gid_t) buf[i];
+
+ if (converted_gids[i] == primary)
+ need_primary = FALSE;
+ }
+
+ if (need_primary && primary != DBUS_GID_UNSET)
+ {
+ converted_gids[n_gids] = primary;
+ n_gids++;
+ }
+
+ _dbus_credentials_take_unix_gids (credentials, converted_gids, n_gids);
+
+out:
+ dbus_free (buf);
+ return !oom;
+#else
+ /* no error */
+ return TRUE;
+#endif
+}
+
+/* return FALSE on OOM, TRUE otherwise, even if no credentials were found */
+static dbus_bool_t
+add_linux_security_label_to_credentials (int client_fd,
+ DBusCredentials *credentials)
+{
+#if defined(__linux__) && defined(SO_PEERSEC)
+ DBusString buf;
+ socklen_t len = 1024;
+ dbus_bool_t oom = FALSE;
+
+ if (!_dbus_string_init_preallocated (&buf, len) ||
+ !_dbus_string_set_length (&buf, len))
+ return FALSE;
+
+ while (getsockopt (client_fd, SOL_SOCKET, SO_PEERSEC,
+ _dbus_string_get_data (&buf), &len) < 0)
+ {
+ int e = errno;
+
+ _dbus_verbose ("getsockopt failed with %s, len now %lu\n",
+ _dbus_strerror (e), (unsigned long) len);
+
+ if (e != ERANGE || len <= _dbus_string_get_length_uint (&buf))
+ {
+ _dbus_verbose ("Failed to getsockopt(SO_PEERSEC): %s\n",
+ _dbus_strerror (e));
+ goto out;
+ }
+
+ /* If not enough space, len is updated to be enough.
+ * Try again with a large enough buffer. */
+ if (!_dbus_string_set_length (&buf, len))
+ {
+ oom = TRUE;
+ goto out;
+ }
+
+ _dbus_verbose ("will try again with %lu\n", (unsigned long) len);
+ }
+
+ if (len <= 0)
+ {
+ _dbus_verbose ("getsockopt(SO_PEERSEC) yielded <= 0 bytes: %lu\n",
+ (unsigned long) len);
+ goto out;
+ }
+
+ if (len > _dbus_string_get_length_uint (&buf))
+ {
+ _dbus_verbose ("%lu > %u", (unsigned long) len,
+ _dbus_string_get_length_uint (&buf));
+ _dbus_assert_not_reached ("getsockopt(SO_PEERSEC) overflowed");
+ }
+
+ if (_dbus_string_get_byte (&buf, len - 1) == 0)
+ {
+ /* the kernel included the trailing \0 in its count,
+ * but DBusString always has an extra \0 after the data anyway */
+ _dbus_verbose ("subtracting trailing \\0\n");
+ len--;
+ }
+
+ if (!_dbus_string_set_length (&buf, len))
+ {
+ _dbus_assert_not_reached ("shortening string should not lead to OOM");
+ oom = TRUE;
+ goto out;
+ }
+
+ if (strlen (_dbus_string_get_const_data (&buf)) != len)
+ {
+ /* LSM people on the linux-security-module@ mailing list say this
+ * should never happen: the label should be a bytestring with
+ * an optional trailing \0 */
+ _dbus_verbose ("security label from kernel had an embedded \\0, "
+ "ignoring it\n");
+ goto out;
+ }
+
+ _dbus_verbose ("getsockopt(SO_PEERSEC): %lu bytes excluding \\0: %s\n",
+ (unsigned long) len,
+ _dbus_string_get_const_data (&buf));
+
+ if (!_dbus_credentials_add_linux_security_label (credentials,
+ _dbus_string_get_const_data (&buf)))
+ {
+ oom = TRUE;
+ goto out;
+ }
+
+out:
+ _dbus_string_free (&buf);
+ return !oom;
+#else
+ /* no error */
+ return TRUE;
+#endif
+}
+
+/**
+ * Reads a single byte which must be nul (an error occurs otherwise),
+ * and reads unix credentials if available. Clears the credentials
+ * object, then adds pid/uid if available, so any previous credentials
+ * stored in the object are lost.
+ *
+ * DBusServer makes the security assumption that the credentials
+ * returned by this method are the credentials that were active
+ * at the time the socket was opened. Do not add APIs to this
+ * method that would break that assumption.
+ *
+ * In particular, it is incorrect to use any API of the form
+ * "get the process ID at the other end of the connection, then
+ * determine its uid, gid, or other credentials from the pid"
+ * (e.g. looking in /proc on Linux). If we did that, we would
+ * be vulnerable to several attacks. A malicious process could
+ * queue up the rest of the authentication handshake and a malicious
+ * message that it should not be allowed to send, then race with
+ * the DBusServer to exec() a more privileged (e.g. setuid) binary that
+ * would have been allowed to send that message; or it could exit,
+ * and arrange for enough setuid processes to be started that its
+ * pid would be recycled for one of those processes with high
+ * probability; or it could fd-pass the connection to a more
+ * privileged process.
+ *
+ * Return value indicates whether a byte was read, not whether
+ * we got valid credentials. On some systems, such as Linux,
+ * reading/writing the byte isn't actually required, but we do it
+ * anyway just to avoid multiple codepaths.
+ *
+ * Fails if no byte is available, so you must select() first.
+ *
+ * The point of the byte is that on some systems we have to
+ * use sendmsg()/recvmsg() to transmit credentials.
+ *
+ * @param client_fd the client file descriptor
+ * @param credentials object to add client credentials to
+ * @param error location to store error code
+ * @returns #TRUE on success
+ */
+dbus_bool_t
+_dbus_read_credentials_socket (DBusSocket client_fd,
+ DBusCredentials *credentials,
+ DBusError *error)
+{
+ struct msghdr msg;
+ struct iovec iov;
+ char buf;
+ dbus_uid_t uid_read;
+ dbus_gid_t primary_gid_read;
+ dbus_pid_t pid_read;
+ int bytes_read;
+ int pid_fd_read;
+
+#ifdef HAVE_CMSGCRED
+ union {
+ struct cmsghdr hdr;
+ char cred[CMSG_SPACE (sizeof (struct cmsgcred))];
+ } cmsg;
+#endif
+
+ /* The POSIX spec certainly doesn't promise this, but
+ * we need these assertions to fail as soon as we're wrong about
+ * it so we can do the porting fixups
+ */
+ _DBUS_STATIC_ASSERT (sizeof (pid_t) <= sizeof (dbus_pid_t));
+ _DBUS_STATIC_ASSERT (sizeof (uid_t) <= sizeof (dbus_uid_t));
+ _DBUS_STATIC_ASSERT (sizeof (gid_t) <= sizeof (dbus_gid_t));
+
+ uid_read = DBUS_UID_UNSET;
+ primary_gid_read = DBUS_GID_UNSET;
+ pid_read = DBUS_PID_UNSET;
+ pid_fd_read = -1;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ _dbus_credentials_clear (credentials);
+
+ iov.iov_base = &buf;
+ iov.iov_len = 1;
+
+ _DBUS_ZERO(msg);
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+#if defined(HAVE_CMSGCRED)
+ _DBUS_ZERO(cmsg);
+ msg.msg_control = (caddr_t) &cmsg;
+ msg.msg_controllen = CMSG_SPACE (sizeof (struct cmsgcred));
+#endif
+
+ again:
+ bytes_read = recvmsg (client_fd.fd, &msg, 0);
+
+ if (bytes_read < 0)
+ {
+ if (errno == EINTR)
+ goto again;
+
+ /* EAGAIN or EWOULDBLOCK would be unexpected here since we would
+ * normally only call read_credentials if the socket was ready
+ * for reading
+ */
+
+ dbus_set_error (error, _dbus_error_from_errno (errno),
+ "Failed to read credentials byte: %s",
+ _dbus_strerror (errno));
+ return FALSE;
+ }
+ else if (bytes_read == 0)
+ {
+ /* this should not happen unless we are using recvmsg wrong,
+ * so is essentially here for paranoia
+ */
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "Failed to read credentials byte (zero-length read)");
+ return FALSE;
+ }
+ else if (buf != '\0')
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "Credentials byte was not nul");
+ return FALSE;
+ }
+
+ _dbus_verbose ("read credentials byte\n");
+
+ {
+#ifdef SO_PEERCRED
+ /* Supported by at least Linux and OpenBSD, with minor differences.
+ *
+ * This mechanism passes the process ID through and does not require
+ * the peer's cooperation, so we prefer it over all others. Notably,
+ * Linux also supports SCM_CREDENTIALS, which is similar to FreeBSD
+ * SCM_CREDS; it's implemented in GIO, but we don't use it in dbus at all,
+ * because this is much less fragile.
+ */
+#ifdef __OpenBSD__
+ struct sockpeercred cr;
+#else
+ struct ucred cr;
+#endif
+ socklen_t cr_len = sizeof (cr);
+
+ if (getsockopt (client_fd.fd, SOL_SOCKET, SO_PEERCRED, &cr, &cr_len) != 0)
+ {
+ _dbus_verbose ("Failed to getsockopt(SO_PEERCRED): %s\n",
+ _dbus_strerror (errno));
+ }
+ else if (cr_len != sizeof (cr))
+ {
+ _dbus_verbose ("Failed to getsockopt(SO_PEERCRED), returned %d bytes, expected %d\n",
+ cr_len, (int) sizeof (cr));
+ }
+ else
+ {
+ pid_read = cr.pid;
+ uid_read = cr.uid;
+#ifdef __linux__
+ /* Do other platforms have cr.gid? (Not that it really matters,
+ * because the gid is useless to us unless we know the complete
+ * group vector, which we only know on Linux.) */
+ primary_gid_read = cr.gid;
+#endif
+ }
+
+#ifdef SO_PEERPIDFD
+ /* If we have SO_PEERCRED we might also have SO_PEERPIDFD, which
+ * allows to pin the process ID, and is available on Linux since v6.5. */
+ cr_len = sizeof (int);
+
+ if (getsockopt (client_fd.fd, SOL_SOCKET, SO_PEERPIDFD, &pid_fd_read, &cr_len) != 0)
+ {
+ _dbus_verbose ("Failed to getsockopt(SO_PEERPIDFD): %s\n",
+ _dbus_strerror (errno));
+ }
+ else if (cr_len != sizeof (int))
+ {
+ _dbus_verbose ("Failed to getsockopt(SO_PEERPIDFD), returned %d bytes, expected %d\n",
+ cr_len, (int) sizeof (int));
+ }
+#endif
+
+#elif defined(HAVE_UNPCBID) && defined(LOCAL_PEEREID)
+ /* Another variant of the above - used on NetBSD
+ */
+ struct unpcbid cr;
+ socklen_t cr_len = sizeof (cr);
+
+ if (getsockopt (client_fd.fd, 0, LOCAL_PEEREID, &cr, &cr_len) != 0)
+ {
+ _dbus_verbose ("Failed to getsockopt(LOCAL_PEEREID): %s\n",
+ _dbus_strerror (errno));
+ }
+ else if (cr_len != sizeof (cr))
+ {
+ _dbus_verbose ("Failed to getsockopt(LOCAL_PEEREID), returned %d bytes, expected %d\n",
+ cr_len, (int) sizeof (cr));
+ }
+ else
+ {
+ pid_read = cr.unp_pid;
+ uid_read = cr.unp_euid;
+ }
+#elif defined(HAVE_CMSGCRED)
+ /* We only check for HAVE_CMSGCRED, but we're really assuming that the
+ * presence of that struct implies SCM_CREDS. Supported by at least
+ * FreeBSD and DragonflyBSD.
+ *
+ * This mechanism requires the peer to help us (it has to send us a
+ * SCM_CREDS message) but it does pass the process ID through,
+ * which makes it better than getpeereid().
+ */
+ struct cmsgcred *cred;
+ struct cmsghdr *cmsgp;
+
+ for (cmsgp = CMSG_FIRSTHDR (&msg);
+ cmsgp != NULL;
+ cmsgp = CMSG_NXTHDR (&msg, cmsgp))
+ {
+ if (cmsgp->cmsg_type == SCM_CREDS &&
+ cmsgp->cmsg_level == SOL_SOCKET &&
+ cmsgp->cmsg_len >= CMSG_LEN (sizeof (struct cmsgcred)))
+ {
+ cred = (struct cmsgcred *) (void *) CMSG_DATA (cmsgp);
+ pid_read = cred->cmcred_pid;
+ uid_read = cred->cmcred_euid;
+ break;
+ }
+ }
+
+#elif defined(HAVE_GETPEERUCRED)
+ /* Supported in at least Solaris >= 10. It should probably be higher
+ * up this list, because it carries the pid and we use this code path
+ * for audit data. */
+ ucred_t * ucred = NULL;
+ if (getpeerucred (client_fd.fd, &ucred) == 0)
+ {
+#ifdef HAVE_ADT
+ adt_session_data_t *adth = NULL;
+#endif
+ pid_read = ucred_getpid (ucred);
+ uid_read = ucred_geteuid (ucred);
+#ifdef HAVE_ADT
+ /* generate audit session data based on socket ucred */
+ if (adt_start_session (&adth, NULL, 0) || (adth == NULL))
+ {
+ _dbus_verbose ("Failed to adt_start_session(): %s\n", _dbus_strerror (errno));
+ }
+ else
+ {
+ if (adt_set_from_ucred (adth, ucred, ADT_NEW))
+ {
+ _dbus_verbose ("Failed to adt_set_from_ucred(): %s\n", _dbus_strerror (errno));
+ }
+ else
+ {
+ adt_export_data_t *data = NULL;
+ size_t size = adt_export_session_data (adth, &data);
+ if (size <= 0)
+ {
+ _dbus_verbose ("Failed to adt_export_session_data(): %s\n", _dbus_strerror (errno));
+ }
+ else
+ {
+ _dbus_credentials_add_adt_audit_data (credentials, data, size);
+ free (data);
+ }
+ }
+ (void) adt_end_session (adth);
+ }
+#endif /* HAVE_ADT */
+ }
+ else
+ {
+ _dbus_verbose ("Failed to getpeerucred() credentials: %s\n", _dbus_strerror (errno));
+ }
+ if (ucred != NULL)
+ ucred_free (ucred);
+
+ /* ----------------------------------------------------------------
+ * When adding new mechanisms, please add them above this point
+ * if they support passing the process ID through, or below if not.
+ * ---------------------------------------------------------------- */
+
+#elif defined(HAVE_GETPEEREID)
+ /* getpeereid() originates from D.J. Bernstein and is fairly
+ * widely-supported. According to a web search, it might be present in
+ * any/all of:
+ *
+ * - AIX?
+ * - Blackberry?
+ * - Cygwin
+ * - FreeBSD 4.6+ (but we prefer SCM_CREDS: it carries the pid)
+ * - Mac OS X
+ * - Minix 3.1.8+
+ * - MirBSD?
+ * - NetBSD 5.0+ (but LOCAL_PEEREID would be better: it carries the pid)
+ * - OpenBSD 3.0+ (but we prefer SO_PEERCRED: it carries the pid)
+ * - QNX?
+ */
+ uid_t euid;
+ gid_t egid;
+ if (getpeereid (client_fd.fd, &euid, &egid) == 0)
+ {
+ uid_read = euid;
+ }
+ else
+ {
+ _dbus_verbose ("Failed to getpeereid() credentials: %s\n", _dbus_strerror (errno));
+ }
+#else /* no supported mechanism */
+
+#warning Socket credentials not supported on this Unix OS
+#warning Please tell https://gitlab.freedesktop.org/dbus/dbus/-/issues/new
+
+ /* Please add other operating systems known to support at least one of
+ * the mechanisms above to this list, keeping alphabetical order.
+ * Everything not in this list is best-effort.
+ */
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \
+ defined(__linux__) || \
+ defined(__OpenBSD__) || \
+ defined(__NetBSD__)
+# error Credentials passing not working on this OS is a regression!
+#endif
+
+ _dbus_verbose ("Socket credentials not supported on this OS\n");
+#endif
+ }
+
+ _dbus_verbose ("Credentials:"
+ " pid "DBUS_PID_FORMAT
+ " uid "DBUS_UID_FORMAT
+ "\n",
+ pid_read,
+ uid_read);
+
+ /* Assign this first, so we don't have to close it manually in case one of
+ * the next steps fails. */
+ if (pid_fd_read >= 0)
+ _dbus_credentials_take_pid_fd (credentials, pid_fd_read);
+
+ if (pid_read != DBUS_PID_UNSET)
+ {
+ if (!_dbus_credentials_add_pid (credentials, pid_read))
+ {
+ _DBUS_SET_OOM (error);
+ return FALSE;
+ }
+ }
+
+ if (uid_read != DBUS_UID_UNSET)
+ {
+ if (!_dbus_credentials_add_unix_uid (credentials, uid_read))
+ {
+ _DBUS_SET_OOM (error);
+ return FALSE;
+ }
+ }
+
+ if (!add_linux_security_label_to_credentials (client_fd.fd, credentials))
+ {
+ _DBUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ /* We don't put any groups in the credentials unless we can put them
+ * all there. */
+ if (!add_groups_to_credentials (client_fd.fd, credentials, primary_gid_read))
+ {
+ _DBUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**
+ * Sends a single nul byte with our UNIX credentials as ancillary
+ * data. Returns #TRUE if the data was successfully written. On
+ * systems that don't support sending credentials, just writes a byte,
+ * doesn't send any credentials. On some systems, such as Linux,
+ * reading/writing the byte isn't actually required, but we do it
+ * anyway just to avoid multiple codepaths.
+ *
+ * Fails if no byte can be written, so you must select() first.
+ *
+ * The point of the byte is that on some systems we have to
+ * use sendmsg()/recvmsg() to transmit credentials.
+ *
+ * @param server_fd file descriptor for connection to server
+ * @param error return location for error code
+ * @returns #TRUE if the byte was sent
+ */
+dbus_bool_t
+_dbus_send_credentials_socket (DBusSocket server_fd,
+ DBusError *error)
+{
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ if (write_credentials_byte (server_fd.fd, error))
+ return TRUE;
+ else
+ return FALSE;
+}
+
+/**
+ * Accepts a connection on a listening socket.
+ * Handles EINTR for you.
+ *
+ * This will enable FD_CLOEXEC for the returned socket.
+ *
+ * @param listen_fd the listen file descriptor
+ * @returns the connection fd of the client, or -1 on error
+ */
+DBusSocket
+_dbus_accept (DBusSocket listen_fd)
+{
+ DBusSocket client_fd;
+ struct sockaddr addr;
+ socklen_t addrlen;
+#ifdef HAVE_ACCEPT4
+ dbus_bool_t cloexec_done;
+#endif
+
+ addrlen = sizeof (addr);
+
+ retry:
+
+#ifdef HAVE_ACCEPT4
+ /*
+ * At compile-time, we assume that if accept4() is available in
+ * libc headers, SOCK_CLOEXEC is too. At runtime, it is still
+ * not necessarily true that either is supported by the running kernel.
+ */
+ client_fd.fd = accept4 (listen_fd.fd, &addr, &addrlen, SOCK_CLOEXEC);
+ cloexec_done = client_fd.fd >= 0;
+
+ if (client_fd.fd < 0 && (errno == ENOSYS || errno == EINVAL))
+#endif
+ {
+ client_fd.fd = accept (listen_fd.fd, &addr, &addrlen);
+ }
+
+ if (client_fd.fd < 0)
+ {
+ if (errno == EINTR)
+ goto retry;
+ }
+
+ _dbus_verbose ("client fd %d accepted\n", client_fd.fd);
+
+#ifdef HAVE_ACCEPT4
+ if (!cloexec_done)
+#endif
+ {
+ _dbus_fd_set_close_on_exec(client_fd.fd);
+ }
+
+ return client_fd;
+}
+
+/**
+ * Checks to make sure the given directory is
+ * private to the user
+ *
+ * @param dir the name of the directory
+ * @param error error return
+ * @returns #FALSE on failure
+ **/
+dbus_bool_t
+_dbus_check_dir_is_private_to_user (DBusString *dir, DBusError *error)
+{
+ const char *directory;
+ struct stat sb;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ directory = _dbus_string_get_const_data (dir);
+
+ if (stat (directory, &sb) < 0)
+ {
+ dbus_set_error (error, _dbus_error_from_errno (errno),
+ "%s", _dbus_strerror (errno));
+
+ return FALSE;
+ }
+
+ if (sb.st_uid != geteuid ())
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "%s directory is owned by user %lu, not %lu",
+ directory,
+ (unsigned long) sb.st_uid,
+ (unsigned long) geteuid ());
+ return FALSE;
+ }
+
+ if ((S_IROTH & sb.st_mode) || (S_IWOTH & sb.st_mode) ||
+ (S_IRGRP & sb.st_mode) || (S_IWGRP & sb.st_mode))
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "%s directory is not private to the user", directory);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static dbus_bool_t
+fill_user_info_from_passwd (struct passwd *p,
+ DBusUserInfo *info,
+ DBusError *error)
+{
+ _dbus_assert (p->pw_name != NULL);
+ _dbus_assert (p->pw_dir != NULL);
+
+ info->uid = p->pw_uid;
+ info->primary_gid = p->pw_gid;
+ info->username = _dbus_strdup (p->pw_name);
+ info->homedir = _dbus_strdup (p->pw_dir);
+
+ if (info->username == NULL ||
+ info->homedir == NULL)
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static dbus_bool_t
+fill_user_info (DBusUserInfo *info,
+ dbus_uid_t uid,
+ const DBusString *username,
+ DBusError *error)
+{
+ const char *username_c;
+
+ /* exactly one of username/uid provided */
+ _dbus_assert (username != NULL || uid != DBUS_UID_UNSET);
+ _dbus_assert (username == NULL || uid == DBUS_UID_UNSET);
+
+ info->uid = DBUS_UID_UNSET;
+ info->primary_gid = DBUS_GID_UNSET;
+ info->group_ids = NULL;
+ info->n_group_ids = 0;
+ info->username = NULL;
+ info->homedir = NULL;
+
+ if (username != NULL)
+ username_c = _dbus_string_get_const_data (username);
+ else
+ username_c = NULL;
+
+ /* For now assuming that the getpwnam() and getpwuid() flavors
+ * are always symmetrical, if not we have to add more configure
+ * checks
+ */
+
+ {
+ struct passwd *p;
+ char *buf = NULL;
+ int result;
+#ifdef HAVE_GETPWNAM_R
+ size_t buflen;
+ struct passwd p_str;
+
+ /* retrieve maximum needed size for buf */
+ buflen = sysconf (_SC_GETPW_R_SIZE_MAX);
+
+ /* sysconf actually returns a long, but everything else expects size_t,
+ * so just recast here.
+ * https://bugs.freedesktop.org/show_bug.cgi?id=17061
+ */
+ if ((long) buflen <= 0)
+ buflen = 1024;
+
+ result = -1;
+ while (1)
+ {
+ buf = dbus_malloc (buflen);
+ if (buf == NULL)
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ return FALSE;
+ }
+
+ p = NULL;
+ if (uid != DBUS_UID_UNSET)
+ result = getpwuid_r (uid, &p_str, buf, buflen,
+ &p);
+ else
+ result = getpwnam_r (username_c, &p_str, buf, buflen,
+ &p);
+ //Try a bigger buffer if ERANGE was returned
+ if (result == ERANGE && buflen < 512 * 1024)
+ {
+ dbus_free (buf);
+ buflen *= 2;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ /* There are three possibilities:
+ * - an error: result is a nonzero error code, p should be NULL
+ * - name or uid not found: result is 0, p is NULL
+ * - success: result is 0, p should be &p_str
+ *
+ * Ensure that in all failure cases, p is set to NULL, matching the
+ * getpwuid/getpwnam interface. */
+ if (result != 0 || p != &p_str)
+ p = NULL;
+
+#else /* ! HAVE_GETPWNAM_R */
+ /* I guess we're screwed on thread safety here */
+#warning getpwnam_r() not available, please report this to the dbus maintainers with details of your OS
+
+ /* It is unspecified whether "failed to find" counts as an error,
+ * or whether it's reported as p == NULL without touching errno.
+ * Reset errno so we can distinguish. */
+ errno = 0;
+
+ if (uid != DBUS_UID_UNSET)
+ p = getpwuid (uid);
+ else
+ p = getpwnam (username_c);
+
+ /* Always initialized, but only meaningful if p is NULL */
+ result = errno;
+#endif /* ! HAVE_GETPWNAM_R */
+
+ if (p != NULL)
+ {
+ if (!fill_user_info_from_passwd (p, info, error))
+ {
+ dbus_free (buf);
+ return FALSE;
+ }
+ dbus_free (buf);
+ }
+ else
+ {
+ DBusError local_error = DBUS_ERROR_INIT;
+ const char *error_str;
+
+ if (result == 0)
+ error_str = "not found";
+ else
+ error_str = _dbus_strerror (result);
+
+ if (uid != DBUS_UID_UNSET)
+ dbus_set_error (&local_error, _dbus_error_from_errno (result),
+ "Looking up user ID " DBUS_UID_FORMAT ": %s",
+ uid, error_str);
+ else
+ dbus_set_error (&local_error, _dbus_error_from_errno (result),
+ "Looking up user \"%s\": %s",
+ username_c ? username_c : "???", error_str);
+
+ _dbus_verbose ("%s", local_error.message);
+ dbus_move_error (&local_error, error);
+ dbus_free (buf);
+ return FALSE;
+ }
+ }
+
+ /* Fill this in so we can use it to get groups */
+ username_c = info->username;
+
+#ifdef HAVE_GETGROUPLIST
+ {
+ gid_t *buf;
+ int buf_count;
+ int i;
+ int initial_buf_count;
+
+ initial_buf_count = 17;
+ buf_count = initial_buf_count;
+ buf = dbus_new (gid_t, buf_count);
+ if (buf == NULL)
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ goto failed;
+ }
+
+ if (getgrouplist (username_c,
+ info->primary_gid,
+ buf, &buf_count) < 0)
+ {
+ gid_t *new;
+ /* Presumed cause of negative return code: buf has insufficient
+ entries to hold the entire group list. The Linux behavior in this
+ case is to pass back the actual number of groups in buf_count, but
+ on Mac OS X 10.5, buf_count is unhelpfully left alone.
+ So as a hack, try to help out a bit by guessing a larger
+ number of groups, within reason.. might still fail, of course,
+ but we can at least print a more informative message. I looked up
+ the "right way" to do this by downloading Apple's own source code
+ for the "id" command, and it turns out that they use an
+ undocumented library function getgrouplist_2 (!) which is not
+ declared in any header in /usr/include (!!). That did not seem
+ like the way to go here.
+ */
+ if (buf_count == initial_buf_count)
+ {
+ buf_count *= 16; /* Retry with an arbitrarily scaled-up array */
+ }
+ new = dbus_realloc (buf, buf_count * sizeof (buf[0]));
+ if (new == NULL)
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ dbus_free (buf);
+ goto failed;
+ }
+
+ buf = new;
+
+ errno = 0;
+ if (getgrouplist (username_c, info->primary_gid, buf, &buf_count) < 0)
+ {
+ if (errno == 0)
+ {
+ _dbus_warn ("It appears that username \"%s\" is in more than %d groups.\nProceeding with just the first %d groups.",
+ username_c, buf_count, buf_count);
+ }
+ else
+ {
+ dbus_set_error (error,
+ _dbus_error_from_errno (errno),
+ "Failed to get groups for username \"%s\" primary GID "
+ DBUS_GID_FORMAT ": %s\n",
+ username_c, info->primary_gid,
+ _dbus_strerror (errno));
+ dbus_free (buf);
+ goto failed;
+ }
+ }
+ }
+
+ info->group_ids = dbus_new (dbus_gid_t, buf_count);
+ if (info->group_ids == NULL)
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ dbus_free (buf);
+ goto failed;
+ }
+
+ for (i = 0; i < buf_count; ++i)
+ info->group_ids[i] = buf[i];
+
+ info->n_group_ids = buf_count;
+
+ dbus_free (buf);
+ }
+#else /* HAVE_GETGROUPLIST */
+ {
+ /* We just get the one group ID */
+ info->group_ids = dbus_new (dbus_gid_t, 1);
+ if (info->group_ids == NULL)
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ goto failed;
+ }
+
+ info->n_group_ids = 1;
+
+ (info->group_ids)[0] = info->primary_gid;
+ }
+#endif /* HAVE_GETGROUPLIST */
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ return TRUE;
+
+ failed:
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ return FALSE;
+}
+
+/**
+ * Gets user info for the given username.
+ *
+ * @param info user info object to initialize
+ * @param username the username
+ * @param error error return
+ * @returns #TRUE on success
+ */
+dbus_bool_t
+_dbus_user_info_fill (DBusUserInfo *info,
+ const DBusString *username,
+ DBusError *error)
+{
+ return fill_user_info (info, DBUS_UID_UNSET,
+ username, error);
+}
+
+/**
+ * Gets user info for the given user ID.
+ *
+ * @param info user info object to initialize
+ * @param uid the user ID
+ * @param error error return
+ * @returns #TRUE on success
+ */
+dbus_bool_t
+_dbus_user_info_fill_uid (DBusUserInfo *info,
+ dbus_uid_t uid,
+ DBusError *error)
+{
+ return fill_user_info (info, uid,
+ NULL, error);
+}
+
+/**
+ * Adds the most important credentials of the current process
+ * (the uid and pid) to the passed-in credentials object.
+ *
+ * The group vector is not included because it is rarely needed.
+ * The Linux security label is not included because it is rarely
+ * needed, it requires reading /proc, and the LSM API doesn't actually
+ * guarantee that the string seen in /proc is comparable to the strings
+ * found in SO_PEERSEC results.
+ *
+ * @param credentials credentials to add to
+ * @returns #FALSE if no memory; does not properly roll back on failure, so only some credentials may have been added
+ */
+dbus_bool_t
+_dbus_credentials_add_from_current_process (DBusCredentials *credentials)
+{
+ dbus_pid_t pid = _dbus_getpid ();
+
+ /* The POSIX spec certainly doesn't promise this, but
+ * we need these assertions to fail as soon as we're wrong about
+ * it so we can do the porting fixups
+ */
+ _DBUS_STATIC_ASSERT (sizeof (pid_t) <= sizeof (dbus_pid_t));
+ _DBUS_STATIC_ASSERT (sizeof (uid_t) <= sizeof (dbus_uid_t));
+ _DBUS_STATIC_ASSERT (sizeof (gid_t) <= sizeof (dbus_gid_t));
+
+#if HAVE_DECL_SYS_PIDFD_OPEN
+ /* Normally this syscall would have a race condition, but we can trust
+ * that our own process isn't going to exit, so the pid won't get reused. */
+ int pid_fd = (int) syscall (SYS_pidfd_open, pid, 0);
+ if (pid_fd >= 0)
+ _dbus_credentials_take_pid_fd (credentials, pid_fd);
+#endif
+ if (!_dbus_credentials_add_pid (credentials, pid))
+ return FALSE;
+ if (!_dbus_credentials_add_unix_uid(credentials, _dbus_geteuid()))
+ return FALSE;
+
+ return TRUE;
+}
+
+/**
+ * Resolve the PID from the PID FD, if any. This allows us to avoid
+ * PID reuse attacks. Returns DBUS_PID_UNSET if the PID could not be resolved.
+ * Note that this requires being able to read /proc/self/fdinfo/<FD>,
+ * which is created as 600 and owned by the original UID that the
+ * process started as. So it cannot work when the start as root and
+ * drop privileges mechanism is in use (the systemd unit no longer
+ * does this, but third-party init-scripts might).
+ *
+ * @param pid_fd the PID FD
+ * @returns the resolved PID if found, DBUS_PID_UNSET otherwise
+ */
+dbus_pid_t
+_dbus_resolve_pid_fd (int pid_fd)
+{
+#ifdef __linux__
+ DBusError error = DBUS_ERROR_INIT;
+ DBusString content = _DBUS_STRING_INIT_INVALID;
+ DBusString filename = _DBUS_STRING_INIT_INVALID;
+ dbus_pid_t result = DBUS_PID_UNSET;
+ int pid_index;
+
+ if (pid_fd < 0)
+ goto out;
+
+ if (!_dbus_string_init (&content))
+ goto out;
+
+ if (!_dbus_string_init (&filename))
+ goto out;
+
+ if (!_dbus_string_append_printf (&filename, "/proc/self/fdinfo/%d", pid_fd))
+ goto out;
+
+ if (!_dbus_file_get_contents (&content, &filename, &error))
+ {
+ _dbus_verbose ("Cannot read '/proc/self/fdinfo/%d', unable to resolve PID, %s: %s\n",
+ pid_fd, error.name, error.message);
+ goto out;
+ }
+
+ /* Ensure we are not reading PPid, either it's the first line of the file or
+ * there's a newline before it. */
+ if (!_dbus_string_find (&content, 0, "Pid:", &pid_index) ||
+ (pid_index > 0 && _dbus_string_get_byte (&content, pid_index - 1) != '\n'))
+ {
+ _dbus_verbose ("Cannot find 'Pid:' in '/proc/self/fdinfo/%d', unable to resolve PID\n",
+ pid_fd);
+ goto out;
+ }
+
+ if (!_dbus_string_parse_uint (&content, pid_index + strlen ("Pid:"), &result, NULL))
+ {
+ _dbus_verbose ("Cannot parse 'Pid:' from '/proc/self/fdinfo/%d', unable to resolve PID\n",
+ pid_fd);
+ goto out;
+ }
+
+out:
+ _dbus_string_free (&content);
+ _dbus_string_free (&filename);
+ dbus_error_free (&error);
+
+ if (result <= 0)
+ return DBUS_PID_UNSET;
+
+ return result;
+#else
+ return DBUS_PID_UNSET;
+#endif
+
+}
+
+/**
+ * Append to the string the identity we would like to have when we
+ * authenticate, on UNIX this is the current process UID and on
+ * Windows something else, probably a Windows SID string. No escaping
+ * is required, that is done in dbus-auth.c. The username here
+ * need not be anything human-readable, it can be the machine-readable
+ * form i.e. a user id.
+ *
+ * @param str the string to append to
+ * @returns #FALSE on no memory
+ */
+dbus_bool_t
+_dbus_append_user_from_current_process (DBusString *str)
+{
+ return _dbus_string_append_uint (str,
+ _dbus_geteuid ());
+}
+
+/**
+ * Gets our process ID
+ * @returns process ID
+ */
+dbus_pid_t
+_dbus_getpid (void)
+{
+ return getpid ();
+}
+
+/** Gets our UID
+ * @returns process UID
+ */
+dbus_uid_t
+_dbus_getuid (void)
+{
+ return getuid ();
+}
+
+/** Gets our effective UID
+ * @returns process effective UID
+ */
+dbus_uid_t
+_dbus_geteuid (void)
+{
+ return geteuid ();
+}
+
+/**
+ * The only reason this is separate from _dbus_getpid() is to allow it
+ * on Windows for logging but not for other purposes.
+ *
+ * @returns process ID to put in log messages
+ */
+unsigned long
+_dbus_pid_for_log (void)
+{
+ return getpid ();
+}
+
+#if !defined(HAVE_STDATOMIC_H) && !DBUS_USE_SYNC
+/* To be thread-safe by default on platforms that don't necessarily have
+ * atomic operations (notably Debian armel, which is armv4t), we must
+ * use a mutex that can be initialized statically, like this.
+ * GLib >= 2.32 uses a similar system.
+ */
+static pthread_mutex_t atomic_mutex = PTHREAD_MUTEX_INITIALIZER;
+#endif
+
+/**
+ * Atomically increments an integer
+ *
+ * @param atomic pointer to the integer to increment
+ * @returns the value before incrementing
+ */
+dbus_int32_t
+_dbus_atomic_inc (DBusAtomic *atomic)
+{
+#ifdef HAVE_STDATOMIC_H
+ /* Atomic version of "old = *atomic; *atomic += 1; return old" */
+ return atomic_fetch_add (&atomic->value, 1);
+#elif DBUS_USE_SYNC
+ /* Atomic version of "*atomic += 1; return *atomic - 1" */
+ return __sync_add_and_fetch(&atomic->value, 1)-1;
+#else
+ dbus_int32_t res;
+
+ pthread_mutex_lock (&atomic_mutex);
+ res = atomic->value;
+ atomic->value += 1;
+ pthread_mutex_unlock (&atomic_mutex);
+
+ return res;
+#endif
+}
+
+/**
+ * Atomically decrement an integer
+ *
+ * @param atomic pointer to the integer to decrement
+ * @returns the value before decrementing
+ */
+dbus_int32_t
+_dbus_atomic_dec (DBusAtomic *atomic)
+{
+#ifdef HAVE_STDATOMIC_H
+ /* Atomic version of "old = *atomic; *atomic -= 1; return old" */
+ return atomic_fetch_sub (&atomic->value, 1);
+#elif DBUS_USE_SYNC
+ /* Atomic version of "*atomic -= 1; return *atomic + 1" */
+ return __sync_sub_and_fetch(&atomic->value, 1)+1;
+#else
+ dbus_int32_t res;
+
+ pthread_mutex_lock (&atomic_mutex);
+ res = atomic->value;
+ atomic->value -= 1;
+ pthread_mutex_unlock (&atomic_mutex);
+
+ return res;
+#endif
+}
+
+/**
+ * Atomically get the value of an integer. It may change at any time
+ * thereafter, so this is mostly only useful for assertions.
+ *
+ * @param atomic pointer to the integer to get
+ * @returns the value at this moment
+ */
+dbus_int32_t
+_dbus_atomic_get (DBusAtomic *atomic)
+{
+#ifdef HAVE_STDATOMIC_H
+ /* Atomic version of "return *atomic" */
+ return atomic_load (&atomic->value);
+#elif DBUS_USE_SYNC
+ __sync_synchronize ();
+ return atomic->value;
+#else
+ dbus_int32_t res;
+
+ pthread_mutex_lock (&atomic_mutex);
+ res = atomic->value;
+ pthread_mutex_unlock (&atomic_mutex);
+
+ return res;
+#endif
+}
+
+/**
+ * Atomically set the value of an integer to 0.
+ *
+ * @param atomic pointer to the integer to set
+ */
+void
+_dbus_atomic_set_zero (DBusAtomic *atomic)
+{
+#ifdef HAVE_STDATOMIC_H
+ /* Atomic version of "*atomic = 0" */
+ atomic_store (&atomic->value, 0);
+#elif DBUS_USE_SYNC
+ /* Atomic version of "*atomic &= 0; return *atomic" */
+ __sync_and_and_fetch (&atomic->value, 0);
+#else
+ pthread_mutex_lock (&atomic_mutex);
+ atomic->value = 0;
+ pthread_mutex_unlock (&atomic_mutex);
+#endif
+}
+
+/**
+ * Atomically set the value of an integer to something nonzero.
+ *
+ * @param atomic pointer to the integer to set
+ */
+void
+_dbus_atomic_set_nonzero (DBusAtomic *atomic)
+{
+#ifdef HAVE_STDATOMIC_H
+ /* Atomic version of "*atomic = 1" */
+ atomic_store (&atomic->value, 1);
+#elif DBUS_USE_SYNC
+ /* Atomic version of "*atomic |= 1; return *atomic" */
+ __sync_or_and_fetch (&atomic->value, 1);
+#else
+ pthread_mutex_lock (&atomic_mutex);
+ atomic->value = 1;
+ pthread_mutex_unlock (&atomic_mutex);
+#endif
+}
+
+/**
+ * Wrapper for poll().
+ *
+ * @param fds the file descriptors to poll
+ * @param n_fds number of descriptors in the array
+ * @param timeout_milliseconds timeout or -1 for infinite
+ * @returns numbers of fds with revents, or <0 on error
+ */
+int
+_dbus_poll (DBusPollFD *fds,
+ int n_fds,
+ int timeout_milliseconds)
+{
+#if defined(HAVE_POLL) && !defined(BROKEN_POLL)
+ /* DBusPollFD is a struct pollfd in this code path, so we can just poll() */
+ if (timeout_milliseconds < -1)
+ {
+ timeout_milliseconds = -1;
+ }
+
+ return poll (fds,
+ n_fds,
+ timeout_milliseconds);
+#else /* ! HAVE_POLL */
+ /* Emulate poll() in terms of select() */
+ fd_set read_set, write_set, err_set;
+ int max_fd = 0;
+ int i;
+ struct timeval tv;
+ int ready;
+
+ FD_ZERO (&read_set);
+ FD_ZERO (&write_set);
+ FD_ZERO (&err_set);
+
+ for (i = 0; i < n_fds; i++)
+ {
+ DBusPollFD *fdp = &fds[i];
+
+ if (fdp->events & _DBUS_POLLIN)
+ FD_SET (fdp->fd, &read_set);
+
+ if (fdp->events & _DBUS_POLLOUT)
+ FD_SET (fdp->fd, &write_set);
+
+ FD_SET (fdp->fd, &err_set);
+
+ max_fd = MAX (max_fd, fdp->fd);
+ }
+
+ tv.tv_sec = timeout_milliseconds / 1000;
+ tv.tv_usec = (timeout_milliseconds % 1000) * 1000;
+
+ ready = select (max_fd + 1, &read_set, &write_set, &err_set,
+ timeout_milliseconds < 0 ? NULL : &tv);
+
+ if (ready > 0)
+ {
+ for (i = 0; i < n_fds; i++)
+ {
+ DBusPollFD *fdp = &fds[i];
+
+ fdp->revents = 0;
+
+ if (FD_ISSET (fdp->fd, &read_set))
+ fdp->revents |= _DBUS_POLLIN;
+
+ if (FD_ISSET (fdp->fd, &write_set))
+ fdp->revents |= _DBUS_POLLOUT;
+
+ if (FD_ISSET (fdp->fd, &err_set))
+ fdp->revents |= _DBUS_POLLERR;
+ }
+ }
+
+ return ready;
+#endif
+}
+
+/**
+ * Get current time, as in gettimeofday(). Use the monotonic clock if
+ * available, to avoid problems when the system time changes.
+ *
+ * @param tv_sec return location for number of seconds
+ * @param tv_usec return location for number of microseconds
+ */
+void
+_dbus_get_monotonic_time (dbus_int64_t *tv_sec,
+ long *tv_usec)
+{
+#ifdef HAVE_MONOTONIC_CLOCK
+ struct timespec ts;
+ clock_gettime (CLOCK_MONOTONIC, &ts);
+
+ if (tv_sec)
+ *tv_sec = ts.tv_sec;
+ if (tv_usec)
+ *tv_usec = ts.tv_nsec / 1000;
+#else
+ struct timeval t;
+
+ gettimeofday (&t, NULL);
+
+ if (tv_sec)
+ *tv_sec = t.tv_sec;
+ if (tv_usec)
+ *tv_usec = t.tv_usec;
+#endif
+}
+
+/**
+ * Get current time, as in gettimeofday(). Never uses the monotonic
+ * clock.
+ *
+ * @param tv_sec return location for number of seconds
+ * @param tv_usec return location for number of microseconds
+ */
+void
+_dbus_get_real_time (dbus_int64_t *tv_sec,
+ long *tv_usec)
+{
+ struct timeval t;
+
+ gettimeofday (&t, NULL);
+
+ if (tv_sec)
+ *tv_sec = t.tv_sec;
+ if (tv_usec)
+ *tv_usec = t.tv_usec;
+}
+
+/**
+ * Creates a directory; succeeds if the directory
+ * is created or already existed.
+ *
+ * @param filename directory filename
+ * @param error initialized error object
+ * @returns #TRUE on success
+ */
+dbus_bool_t
+_dbus_ensure_directory (const DBusString *filename,
+ DBusError *error)
+{
+ const char *filename_c;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ filename_c = _dbus_string_get_const_data (filename);
+
+ if (mkdir (filename_c, 0700) < 0)
+ {
+ if (errno == EEXIST)
+ return TRUE;
+
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "Failed to create directory %s: %s\n",
+ filename_c, _dbus_strerror (errno));
+ return FALSE;
+ }
+ else
+ return TRUE;
+}
+
+/**
+ * Creates a directory. Unlike _dbus_ensure_directory(), this only succeeds
+ * if the directory is genuinely newly-created.
+ *
+ * @param filename directory filename
+ * @param error initialized error object
+ * @returns #TRUE on success
+ */
+dbus_bool_t
+_dbus_create_directory (const DBusString *filename,
+ DBusError *error)
+{
+ const char *filename_c;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ filename_c = _dbus_string_get_const_data (filename);
+
+ if (mkdir (filename_c, 0700) < 0)
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "Failed to create directory %s: %s\n",
+ filename_c, _dbus_strerror (errno));
+ return FALSE;
+ }
+ else
+ return TRUE;
+}
+
+/**
+ * Appends the given filename to the given directory.
+ *
+ * @todo it might be cute to collapse multiple '/' such as "foo//"
+ * concat "//bar"
+ *
+ * @param dir the directory name
+ * @param next_component the filename
+ * @returns #TRUE on success
+ */
+dbus_bool_t
+_dbus_concat_dir_and_file (DBusString *dir,
+ const DBusString *next_component)
+{
+ dbus_bool_t dir_ends_in_slash;
+ dbus_bool_t file_starts_with_slash;
+
+ if (_dbus_string_get_length (dir) == 0 ||
+ _dbus_string_get_length (next_component) == 0)
+ return TRUE;
+
+ dir_ends_in_slash = '/' == _dbus_string_get_byte (dir,
+ _dbus_string_get_length (dir) - 1);
+
+ file_starts_with_slash = '/' == _dbus_string_get_byte (next_component, 0);
+
+ if (dir_ends_in_slash && file_starts_with_slash)
+ {
+ _dbus_string_shorten (dir, 1);
+ }
+ else if (!(dir_ends_in_slash || file_starts_with_slash))
+ {
+ if (!_dbus_string_append_byte (dir, '/'))
+ return FALSE;
+ }
+
+ return _dbus_string_copy (next_component, 0, dir,
+ _dbus_string_get_length (dir));
+}
+
+/** nanoseconds in a second */
+#define NANOSECONDS_PER_SECOND 1000000000
+/** microseconds in a second */
+#define MICROSECONDS_PER_SECOND 1000000
+/** milliseconds in a second */
+#define MILLISECONDS_PER_SECOND 1000
+/** nanoseconds in a millisecond */
+#define NANOSECONDS_PER_MILLISECOND 1000000
+/** microseconds in a millisecond */
+#define MICROSECONDS_PER_MILLISECOND 1000
+
+/**
+ * Sleeps the given number of milliseconds.
+ * @param milliseconds number of milliseconds
+ */
+void
+_dbus_sleep_milliseconds (int milliseconds)
+{
+#ifdef HAVE_NANOSLEEP
+ struct timespec req;
+ struct timespec rem;
+
+ req.tv_sec = milliseconds / MILLISECONDS_PER_SECOND;
+ req.tv_nsec = (milliseconds % MILLISECONDS_PER_SECOND) * NANOSECONDS_PER_MILLISECOND;
+ rem.tv_sec = 0;
+ rem.tv_nsec = 0;
+
+ while (nanosleep (&req, &rem) < 0 && errno == EINTR)
+ req = rem;
+#elif defined (HAVE_USLEEP)
+ usleep (milliseconds * MICROSECONDS_PER_MILLISECOND);
+#else /* ! HAVE_USLEEP */
+ sleep (MAX (milliseconds / 1000, 1));
+#endif
+}
+
+/**
+ * Generates the given number of securely random bytes,
+ * using the best mechanism we can come up with.
+ *
+ * @param str the string
+ * @param n_bytes the number of random bytes to append to string
+ * @param error location to store reason for failure
+ * @returns #TRUE on success, #FALSE on error
+ */
+dbus_bool_t
+_dbus_generate_random_bytes (DBusString *str,
+ int n_bytes,
+ DBusError *error)
+{
+ int old_len = _dbus_string_get_length (str);
+ int fd;
+ int result;
+#ifdef HAVE_GETRANDOM
+ char *buffer;
+
+ if (!_dbus_string_lengthen (str, n_bytes))
+ {
+ _DBUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ buffer = _dbus_string_get_data_len (str, old_len, n_bytes);
+ result = getrandom (buffer, n_bytes, GRND_NONBLOCK);
+
+ if (result == n_bytes)
+ return TRUE;
+
+ _dbus_string_set_length (str, old_len);
+#endif
+
+ /* note, urandom on linux will fall back to pseudorandom */
+ fd = open ("/dev/urandom", O_RDONLY);
+
+ if (fd < 0)
+ {
+ dbus_set_error (error, _dbus_error_from_errno (errno),
+ "Could not open /dev/urandom: %s",
+ _dbus_strerror (errno));
+ return FALSE;
+ }
+
+ _dbus_verbose ("/dev/urandom fd %d opened\n", fd);
+
+ result = _dbus_read (fd, str, n_bytes);
+
+ if (result != n_bytes)
+ {
+ if (result < 0)
+ dbus_set_error (error, _dbus_error_from_errno (errno),
+ "Could not read /dev/urandom: %s",
+ _dbus_strerror (errno));
+ else
+ dbus_set_error (error, DBUS_ERROR_IO_ERROR,
+ "Short read from /dev/urandom");
+
+ _dbus_close (fd, NULL);
+ _dbus_string_set_length (str, old_len);
+ return FALSE;
+ }
+
+ _dbus_verbose ("Read %d bytes from /dev/urandom\n",
+ n_bytes);
+
+ _dbus_close (fd, NULL);
+
+ return TRUE;
+}
+
+/**
+ * Exit the process, returning the given value.
+ *
+ * @param code the exit code
+ */
+void
+_dbus_exit (int code)
+{
+ _exit (code);
+}
+
+/**
+ * A wrapper around strerror() because some platforms
+ * may be lame and not have strerror(). Also, never
+ * returns NULL.
+ *
+ * @param error_number errno.
+ * @returns error description.
+ */
+const char*
+_dbus_strerror (int error_number)
+{
+ const char *msg;
+
+ msg = strerror (error_number);
+ if (msg == NULL)
+ msg = "unknown";
+
+ return msg;
+}
+
+/**
+ * signal (SIGPIPE, SIG_IGN);
+ */
+void
+_dbus_disable_sigpipe (void)
+{
+ signal (SIGPIPE, SIG_IGN);
+}
+
+/**
+ * Sets the file descriptor to be close
+ * on exec. Should be called for all file
+ * descriptors in D-Bus code.
+ *
+ * @param fd the file descriptor
+ */
+void
+_dbus_fd_set_close_on_exec (int fd)
+{
+ int val;
+
+ val = fcntl (fd, F_GETFD, 0);
+
+ if (val < 0)
+ return;
+
+ val |= FD_CLOEXEC;
+
+ fcntl (fd, F_SETFD, val);
+}
+
+/**
+ * Sets the file descriptor to *not* be close-on-exec. This can be called
+ * after _dbus_fd_set_all_close_on_exec() to make exceptions for pipes
+ * used to communicate with child processes.
+ *
+ * @param fd the file descriptor
+ */
+void
+_dbus_fd_clear_close_on_exec (int fd)
+{
+ int val;
+
+ val = fcntl (fd, F_GETFD, 0);
+
+ if (val < 0)
+ return;
+
+ val &= ~FD_CLOEXEC;
+
+ fcntl (fd, F_SETFD, val);
+}
+
+/**
+ * Closes a file descriptor.
+ *
+ * @param fd the file descriptor
+ * @param error error object
+ * @returns #FALSE if error set
+ */
+dbus_bool_t
+_dbus_close (int fd,
+ DBusError *error)
+{
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ again:
+ if (close (fd) < 0)
+ {
+ if (errno == EINTR)
+ goto again;
+
+ dbus_set_error (error, _dbus_error_from_errno (errno),
+ "Could not close fd %d", fd);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**
+ * Duplicates a file descriptor. Makes sure the fd returned is >= 3
+ * (i.e. avoids stdin/stdout/stderr). Sets O_CLOEXEC.
+ *
+ * @param fd the file descriptor to duplicate
+ * @param error address of error location.
+ * @returns duplicated file descriptor
+ * */
+int
+_dbus_dup(int fd,
+ DBusError *error)
+{
+ int new_fd;
+
+#ifdef F_DUPFD_CLOEXEC
+ dbus_bool_t cloexec_done;
+
+ new_fd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
+ cloexec_done = new_fd >= 0;
+
+ if (new_fd < 0 && errno == EINVAL)
+#endif
+ {
+ new_fd = fcntl(fd, F_DUPFD, 3);
+ }
+
+ if (new_fd < 0) {
+
+ dbus_set_error (error, _dbus_error_from_errno (errno),
+ "Could not duplicate fd %d", fd);
+ return -1;
+ }
+
+#ifdef F_DUPFD_CLOEXEC
+ if (!cloexec_done)
+#endif
+ {
+ _dbus_fd_set_close_on_exec(new_fd);
+ }
+
+ return new_fd;
+}
+
+/**
+ * Sets a file descriptor to be nonblocking.
+ *
+ * @param fd the file descriptor.
+ * @param error address of error location.
+ * @returns #TRUE on success.
+ */
+dbus_bool_t
+_dbus_set_socket_nonblocking (DBusSocket fd,
+ DBusError *error)
+{
+ return _dbus_set_fd_nonblocking (fd.fd, error);
+}
+
+static dbus_bool_t
+_dbus_set_fd_nonblocking (int fd,
+ DBusError *error)
+{
+ int val;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ val = fcntl (fd, F_GETFL, 0);
+ if (val < 0)
+ {
+ dbus_set_error (error, _dbus_error_from_errno (errno),
+ "Failed to get flags from file descriptor %d: %s",
+ fd, _dbus_strerror (errno));
+ _dbus_verbose ("Failed to get flags for fd %d: %s\n", fd,
+ _dbus_strerror (errno));
+ return FALSE;
+ }
+
+ if (fcntl (fd, F_SETFL, val | O_NONBLOCK) < 0)
+ {
+ dbus_set_error (error, _dbus_error_from_errno (errno),
+ "Failed to set nonblocking flag of file descriptor %d: %s",
+ fd, _dbus_strerror (errno));
+ _dbus_verbose ("Failed to set fd %d nonblocking: %s\n",
+ fd, _dbus_strerror (errno));
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**
+ * On GNU libc systems, print a crude backtrace to stderr. On other
+ * systems, print "no backtrace support" and block for possible gdb
+ * attachment if an appropriate environment variable is set.
+ */
+void
+_dbus_print_backtrace (void)
+{
+#if defined (HAVE_BACKTRACE) && defined (DBUS_BUILT_R_DYNAMIC)
+ void *bt[500];
+ int bt_size;
+ int i;
+ char **syms;
+
+ bt_size = backtrace (bt, 500);
+
+ syms = backtrace_symbols (bt, bt_size);
+
+ i = 0;
+ while (i < bt_size)
+ {
+ /* don't use dbus_warn since it can _dbus_abort() */
+ fprintf (stderr, " %s\n", syms[i]);
+ ++i;
+ }
+ fflush (stderr);
+
+ free (syms);
+#elif defined (HAVE_BACKTRACE) && ! defined (DBUS_BUILT_R_DYNAMIC)
+ fprintf (stderr, " D-Bus not built with -rdynamic so unable to print a backtrace\n");
+#else
+ fprintf (stderr, " D-Bus not compiled with backtrace support so unable to print a backtrace\n");
+#endif
+}
+
+/**
+ * Creates pair of connect sockets (as in socketpair()).
+ * Sets both ends of the pair nonblocking.
+ *
+ * Marks both file descriptors as close-on-exec
+ *
+ * @param fd1 return location for one end
+ * @param fd2 return location for the other end
+ * @param blocking #TRUE if pair should be blocking
+ * @param error error return
+ * @returns #FALSE on failure (if error is set)
+ */
+dbus_bool_t
+_dbus_socketpair (DBusSocket *fd1,
+ DBusSocket *fd2,
+ dbus_bool_t blocking,
+ DBusError *error)
+{
+#ifdef HAVE_SOCKETPAIR
+ int fds[2];
+ int retval;
+
+#ifdef SOCK_CLOEXEC
+ dbus_bool_t cloexec_done;
+
+ retval = socketpair(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0, fds);
+ cloexec_done = retval >= 0;
+
+ if (retval < 0 && (errno == EINVAL || errno == EPROTOTYPE))
+#endif
+ {
+ retval = socketpair(AF_UNIX, SOCK_STREAM, 0, fds);
+ }
+
+ if (retval < 0)
+ {
+ dbus_set_error (error, _dbus_error_from_errno (errno),
+ "Could not create full-duplex pipe");
+ return FALSE;
+ }
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+#ifdef SOCK_CLOEXEC
+ if (!cloexec_done)
+#endif
+ {
+ _dbus_fd_set_close_on_exec (fds[0]);
+ _dbus_fd_set_close_on_exec (fds[1]);
+ }
+
+ if (!blocking &&
+ (!_dbus_set_fd_nonblocking (fds[0], NULL) ||
+ !_dbus_set_fd_nonblocking (fds[1], NULL)))
+ {
+ dbus_set_error (error, _dbus_error_from_errno (errno),
+ "Could not set full-duplex pipe nonblocking");
+
+ _dbus_close (fds[0], NULL);
+ _dbus_close (fds[1], NULL);
+
+ return FALSE;
+ }
+
+ fd1->fd = fds[0];
+ fd2->fd = fds[1];
+
+ _dbus_verbose ("full-duplex pipe %d <-> %d\n",
+ fd1->fd, fd2->fd);
+
+ return TRUE;
+#else
+ _dbus_warn ("_dbus_socketpair() not implemented on this OS");
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "_dbus_socketpair() not implemented on this OS");
+ return FALSE;
+#endif
+}
+
+/**
+ * Measure the length of the given format string and arguments,
+ * not including the terminating nul.
+ *
+ * @param format a printf-style format string
+ * @param args arguments for the format string
+ * @returns length of the given format string and args, or -1 if no memory
+ */
+int
+_dbus_printf_string_upper_bound (const char *format,
+ va_list args)
+{
+ char static_buf[1024];
+ int bufsize = sizeof (static_buf);
+ int len;
+ va_list args_copy;
+
+ va_copy (args_copy, args);
+ len = vsnprintf (static_buf, bufsize, format, args_copy);
+ va_end (args_copy);
+
+ /* If vsnprintf() returned non-negative, then either the string fits in
+ * static_buf, or this OS has the POSIX and C99 behaviour where vsnprintf
+ * returns the number of characters that were needed, or this OS returns the
+ * truncated length.
+ *
+ * We ignore the possibility that snprintf might just ignore the length and
+ * overrun the buffer (64-bit Solaris 7), because that's pathological.
+ * If your libc is really that bad, come back when you have a better one. */
+ if (len == bufsize)
+ {
+ /* This could be the truncated length (Tru64 and IRIX have this bug),
+ * or the real length could be coincidentally the same. Which is it?
+ * If vsnprintf returns the truncated length, we'll go to the slow
+ * path. */
+ va_copy (args_copy, args);
+
+ if (vsnprintf (static_buf, 1, format, args_copy) == 1)
+ len = -1;
+
+ va_end (args_copy);
+ }
+
+ /* If vsnprintf() returned negative, we have to do more work.
+ * HP-UX returns negative. */
+ while (len < 0)
+ {
+ char *buf;
+
+ bufsize *= 2;
+
+ buf = dbus_malloc (bufsize);
+
+ if (buf == NULL)
+ return -1;
+
+ va_copy (args_copy, args);
+ len = vsnprintf (buf, bufsize, format, args_copy);
+ va_end (args_copy);
+
+ dbus_free (buf);
+
+ /* If the reported length is exactly the buffer size, round up to the
+ * next size, in case vsnprintf has been returning the truncated
+ * length */
+ if (len == bufsize)
+ len = -1;
+ }
+
+ return len;
+}
+
+/**
+ * Gets the temporary files directory by inspecting the environment variables
+ * TMPDIR, TMP, and TEMP in that order. If none of those are set "/tmp" is returned
+ *
+ * @returns location of temp directory, or #NULL if no memory for locking
+ */
+const char*
+_dbus_get_tmpdir(void)
+{
+ /* Protected by _DBUS_LOCK_sysdeps */
+ static const char* tmpdir = NULL;
+
+ if (!_DBUS_LOCK (sysdeps))
+ return NULL;
+
+ if (tmpdir == NULL)
+ {
+ /* TMPDIR is what glibc uses, then
+ * glibc falls back to the P_tmpdir macro which
+ * just expands to "/tmp"
+ */
+ if (tmpdir == NULL)
+ tmpdir = getenv("TMPDIR");
+
+ /* These two env variables are probably
+ * broken, but maybe some OS uses them?
+ */
+ if (tmpdir == NULL)
+ tmpdir = getenv("TMP");
+ if (tmpdir == NULL)
+ tmpdir = getenv("TEMP");
+
+ /* And this is the sane fallback. */
+ if (tmpdir == NULL)
+ tmpdir = "/tmp";
+ }
+
+ _DBUS_UNLOCK (sysdeps);
+
+ _dbus_assert(tmpdir != NULL);
+
+ return tmpdir;
+}
+
+#if defined(DBUS_ENABLE_X11_AUTOLAUNCH) || defined(DBUS_ENABLE_LAUNCHD)
+/**
+ * Execute a subprocess, returning up to 1024 bytes of output
+ * into @p result.
+ *
+ * If successful, returns #TRUE and appends the output to @p
+ * result. If a failure happens, returns #FALSE and
+ * sets an error in @p error.
+ *
+ * @note It's not an error if the subprocess terminates normally
+ * without writing any data to stdout. Verify the @p result length
+ * before and after this function call to cover this case.
+ *
+ * @param progname initial path to exec (may or may not be absolute)
+ * @param path_fallback if %TRUE, search PATH for executable
+ * @param argv NULL-terminated list of arguments
+ * @param result a DBusString where the output can be append
+ * @param error a DBusError to store the error in case of failure
+ * @returns #TRUE on success, #FALSE if an error happened
+ */
+static dbus_bool_t
+_read_subprocess_line_argv (const char *progpath,
+ dbus_bool_t path_fallback,
+ const char * const *argv,
+ DBusString *result,
+ DBusError *error)
+{
+ int result_pipe[2] = { -1, -1 };
+ int errors_pipe[2] = { -1, -1 };
+ pid_t pid;
+ int ret;
+ int status;
+ int orig_len;
+
+ dbus_bool_t retval;
+ sigset_t new_set, old_set;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+ retval = FALSE;
+
+ /* We need to block any existing handlers for SIGCHLD temporarily; they
+ * will cause waitpid() below to fail.
+ * https://bugs.freedesktop.org/show_bug.cgi?id=21347
+ */
+ sigemptyset (&new_set);
+ sigaddset (&new_set, SIGCHLD);
+ sigprocmask (SIG_BLOCK, &new_set, &old_set);
+
+ orig_len = _dbus_string_get_length (result);
+
+#define READ_END 0
+#define WRITE_END 1
+ if (pipe (result_pipe) < 0)
+ {
+ dbus_set_error (error, _dbus_error_from_errno (errno),
+ "Failed to create a pipe to call %s: %s",
+ progpath, _dbus_strerror (errno));
+ _dbus_verbose ("Failed to create a pipe to call %s: %s\n",
+ progpath, _dbus_strerror (errno));
+ goto out;
+ }
+ if (pipe (errors_pipe) < 0)
+ {
+ dbus_set_error (error, _dbus_error_from_errno (errno),
+ "Failed to create a pipe to call %s: %s",
+ progpath, _dbus_strerror (errno));
+ _dbus_verbose ("Failed to create a pipe to call %s: %s\n",
+ progpath, _dbus_strerror (errno));
+ goto out;
+ }
+
+ /* Make sure our output buffers aren't redundantly printed by both the
+ * parent and the child */
+ fflush (stdout);
+ fflush (stderr);
+
+ pid = fork ();
+ if (pid < 0)
+ {
+ dbus_set_error (error, _dbus_error_from_errno (errno),
+ "Failed to fork() to call %s: %s",
+ progpath, _dbus_strerror (errno));
+ _dbus_verbose ("Failed to fork() to call %s: %s\n",
+ progpath, _dbus_strerror (errno));
+ goto out;
+ }
+
+ if (pid == 0)
+ {
+ /* child process */
+ const char *error_str;
+
+ if (!_dbus_ensure_standard_fds (DBUS_FORCE_STDIN_NULL, &error_str))
+ {
+ int saved_errno = errno;
+
+ /* Try to write details into the pipe, but don't bother
+ * trying too hard (no retry loop). */
+
+ if (write (errors_pipe[WRITE_END], error_str, strlen (error_str)) < 0 ||
+ write (errors_pipe[WRITE_END], ": ", 2) < 0)
+ {
+ /* ignore, not much we can do */
+ }
+
+ error_str = _dbus_strerror (saved_errno);
+
+ if (write (errors_pipe[WRITE_END], error_str, strlen (error_str)) < 0)
+ {
+ /* ignore, not much we can do */
+ }
+
+ _exit (1);
+ }
+
+ /* set-up stdXXX */
+ close (result_pipe[READ_END]);
+ close (errors_pipe[READ_END]);
+
+ if (dup2 (result_pipe[WRITE_END], 1) == -1) /* setup stdout */
+ _exit (1);
+ if (dup2 (errors_pipe[WRITE_END], 2) == -1) /* setup stderr */
+ _exit (1);
+
+ _dbus_close_all ();
+
+ sigprocmask (SIG_SETMASK, &old_set, NULL);
+
+ /* If it looks fully-qualified, try execv first */
+ if (progpath[0] == '/')
+ {
+ execv (progpath, (char * const *) argv);
+ /* Ok, that failed. Now if path_fallback is given, let's
+ * try unqualified. This is mostly a hack to work
+ * around systems which ship dbus-launch in /usr/bin
+ * but everything else in /bin (because dbus-launch
+ * depends on X11).
+ */
+ if (path_fallback)
+ /* We must have a slash, because we checked above */
+ execvp (strrchr (progpath, '/')+1, (char * const *) argv);
+ }
+ else
+ execvp (progpath, (char * const *) argv);
+
+ /* still nothing, we failed */
+ _exit (1);
+ }
+
+ /* parent process */
+ close (result_pipe[WRITE_END]);
+ close (errors_pipe[WRITE_END]);
+ result_pipe[WRITE_END] = -1;
+ errors_pipe[WRITE_END] = -1;
+
+ ret = 0;
+ do
+ {
+ ret = _dbus_read (result_pipe[READ_END], result, 1024);
+ }
+ while (ret > 0);
+
+ /* reap the child process to avoid it lingering as zombie */
+ do
+ {
+ ret = waitpid (pid, &status, 0);
+ }
+ while (ret == -1 && errno == EINTR);
+
+ /* We succeeded if the process exited with status 0 and
+ anything was read */
+ if (!WIFEXITED (status) || WEXITSTATUS (status) != 0 )
+ {
+ /* The process ended with error */
+ DBusString error_message;
+ if (!_dbus_string_init (&error_message))
+ {
+ _DBUS_SET_OOM (error);
+ goto out;
+ }
+
+ ret = 0;
+ do
+ {
+ ret = _dbus_read (errors_pipe[READ_END], &error_message, 1024);
+ }
+ while (ret > 0);
+
+ _dbus_string_set_length (result, orig_len);
+ if (_dbus_string_get_length (&error_message) > 0)
+ dbus_set_error (error, DBUS_ERROR_SPAWN_EXEC_FAILED,
+ "%s terminated abnormally with the following error: %s",
+ progpath, _dbus_string_get_data (&error_message));
+ else
+ dbus_set_error (error, DBUS_ERROR_SPAWN_EXEC_FAILED,
+ "%s terminated abnormally without any error message",
+ progpath);
+ goto out;
+ }
+
+ retval = TRUE;
+
+ out:
+ sigprocmask (SIG_SETMASK, &old_set, NULL);
+
+ _DBUS_ASSERT_ERROR_XOR_BOOL (error, retval);
+
+ if (result_pipe[0] != -1)
+ close (result_pipe[0]);
+ if (result_pipe[1] != -1)
+ close (result_pipe[1]);
+ if (errors_pipe[0] != -1)
+ close (errors_pipe[0]);
+ if (errors_pipe[1] != -1)
+ close (errors_pipe[1]);
+
+ return retval;
+}
+#endif
+
+/**
+ * Returns the address of a new session bus.
+ *
+ * If successful, returns #TRUE and appends the address to @p
+ * address. If a failure happens, returns #FALSE and
+ * sets an error in @p error.
+ *
+ * @param scope scope of autolaunch (Windows only)
+ * @param address a DBusString where the address can be stored
+ * @param error a DBusError to store the error in case of failure
+ * @returns #TRUE on success, #FALSE if an error happened
+ */
+dbus_bool_t
+_dbus_get_autolaunch_address (const char *scope,
+ DBusString *address,
+ DBusError *error)
+{
+#ifdef DBUS_ENABLE_X11_AUTOLAUNCH
+ static const char arg_dbus_launch[] = "dbus-launch";
+ static const char arg_autolaunch[] = "--autolaunch";
+ static const char arg_binary_syntax[] = "--binary-syntax";
+ static const char arg_close_stderr[] = "--close-stderr";
+
+ /* Perform X11-based autolaunch. (We also support launchd-based autolaunch,
+ * but that's done elsewhere, and if it worked, this function wouldn't
+ * be called.) */
+ const char *display;
+ const char *progpath;
+ const char *argv[6];
+ int i;
+ DBusString uuid;
+ dbus_bool_t retval;
+
+ if (_dbus_check_setuid ())
+ {
+ dbus_set_error_const (error, DBUS_ERROR_NOT_SUPPORTED,
+ "Unable to autolaunch when setuid");
+ return FALSE;
+ }
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+ retval = FALSE;
+
+ /* fd.o #19997: if $DISPLAY isn't set to something useful, then
+ * dbus-launch-x11 is just going to fail. Rather than trying to
+ * run it, we might as well bail out early with a nice error.
+ *
+ * This is not strictly true in a world where the user bus exists,
+ * because dbus-launch --autolaunch knows how to connect to that -
+ * but if we were going to connect to the user bus, we'd have done
+ * so before trying autolaunch: in any case. */
+ display = _dbus_getenv ("DISPLAY");
+
+ if (display == NULL || display[0] == '\0')
+ {
+ dbus_set_error_const (error, DBUS_ERROR_NOT_SUPPORTED,
+ "Unable to autolaunch a dbus-daemon without a $DISPLAY for X11");
+ return FALSE;
+ }
+
+ if (!_dbus_string_init (&uuid))
+ {
+ _DBUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ if (!_dbus_get_local_machine_uuid_encoded (&uuid, error))
+ {
+ goto out;
+ }
+
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
+ progpath = _dbus_getenv ("DBUS_TEST_DBUS_LAUNCH");
+
+ if (progpath == NULL)
+#endif
+ progpath = DBUS_BINDIR "/dbus-launch";
+ /*
+ * argv[0] is always dbus-launch, that's the name what we'll
+ * get from /proc, or ps(1), regardless what the progpath is,
+ * see fd.o#69716
+ */
+ i = 0;
+ argv[i] = arg_dbus_launch;
+ ++i;
+ argv[i] = arg_autolaunch;
+ ++i;
+ argv[i] = _dbus_string_get_data (&uuid);
+ ++i;
+ argv[i] = arg_binary_syntax;
+ ++i;
+ argv[i] = arg_close_stderr;
+ ++i;
+ argv[i] = NULL;
+ ++i;
+
+ _dbus_assert (i == _DBUS_N_ELEMENTS (argv));
+
+ retval = _read_subprocess_line_argv (progpath,
+ TRUE,
+ argv, address, error);
+
+ out:
+ _dbus_string_free (&uuid);
+ return retval;
+#else
+ dbus_set_error_const (error, DBUS_ERROR_NOT_SUPPORTED,
+ "Using X11 for dbus-daemon autolaunch was disabled at compile time, "
+ "set your DBUS_SESSION_BUS_ADDRESS instead");
+ return FALSE;
+#endif
+}
+
+/**
+ * Reads the uuid of the machine we're running on from
+ * the dbus configuration. Optionally try to create it
+ * (only root can do this usually).
+ *
+ * On UNIX, reads a file that gets created by dbus-uuidgen
+ * in a post-install script. On Windows, if there's a standard
+ * machine uuid we could just use that, but I can't find one
+ * with the right properties (the hardware profile guid can change
+ * without rebooting I believe). If there's no standard one
+ * we might want to use the registry instead of a file for
+ * this, and I'm not sure how we'd ensure the uuid gets created.
+ *
+ * @param machine_id guid to init with the machine's uuid
+ * @param create_if_not_found try to create the uuid if it doesn't exist
+ * @param error the error return
+ * @returns #FALSE if the error is set
+ */
+dbus_bool_t
+_dbus_read_local_machine_uuid (DBusGUID *machine_id,
+ dbus_bool_t create_if_not_found,
+ DBusError *error)
+{
+ DBusError our_error = DBUS_ERROR_INIT;
+ DBusError etc_error = DBUS_ERROR_INIT;
+ DBusString filename;
+ dbus_bool_t b;
+
+ _dbus_string_init_const (&filename, DBUS_MACHINE_UUID_FILE);
+
+ b = _dbus_read_uuid_file (&filename, machine_id, FALSE, &our_error);
+ if (b)
+ return TRUE;
+
+ /* Fallback to the system machine ID */
+ _dbus_string_init_const (&filename, "/etc/machine-id");
+ b = _dbus_read_uuid_file (&filename, machine_id, FALSE, &etc_error);
+
+ if (b)
+ {
+ if (create_if_not_found)
+ {
+ /* try to copy it to the DBUS_MACHINE_UUID_FILE, but do not
+ * complain if that isn't possible for whatever reason */
+ _dbus_string_init_const (&filename, DBUS_MACHINE_UUID_FILE);
+ _dbus_write_uuid_file (&filename, machine_id, NULL);
+ }
+
+ dbus_error_free (&our_error);
+ return TRUE;
+ }
+
+ if (!create_if_not_found)
+ {
+ dbus_set_error (error, etc_error.name,
+ "D-Bus library appears to be incorrectly set up: "
+ "see the manual page for dbus-uuidgen to correct "
+ "this issue. (%s; %s)",
+ our_error.message, etc_error.message);
+ dbus_error_free (&our_error);
+ dbus_error_free (&etc_error);
+ return FALSE;
+ }
+
+ dbus_error_free (&our_error);
+ dbus_error_free (&etc_error);
+
+ /* if none found, try to make a new one */
+ _dbus_string_init_const (&filename, DBUS_MACHINE_UUID_FILE);
+
+ if (!_dbus_generate_uuid (machine_id, error))
+ return FALSE;
+
+ return _dbus_write_uuid_file (&filename, machine_id, error);
+}
+
+/**
+ * quries launchd for a specific env var which holds the socket path.
+ * @param socket_path append the socket path to this DBusString
+ * @param launchd_env_var the env var to look up
+ * @param error a DBusError to store the error in case of failure
+ * @return the value of the env var
+ */
+dbus_bool_t
+_dbus_lookup_launchd_socket (DBusString *socket_path,
+ const char *launchd_env_var,
+ DBusError *error)
+{
+#ifdef DBUS_ENABLE_LAUNCHD
+ char *argv[4];
+ int i;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ if (_dbus_check_setuid ())
+ {
+ dbus_set_error_const (error, DBUS_ERROR_NOT_SUPPORTED,
+ "Unable to find launchd socket when setuid");
+ return FALSE;
+ }
+
+ i = 0;
+ argv[i] = "launchctl";
+ ++i;
+ argv[i] = "getenv";
+ ++i;
+ argv[i] = (char*)launchd_env_var;
+ ++i;
+ argv[i] = NULL;
+ ++i;
+
+ _dbus_assert (i == _DBUS_N_ELEMENTS (argv));
+
+ if (!_read_subprocess_line_argv(argv[0], TRUE, argv, socket_path, error))
+ {
+ return FALSE;
+ }
+
+ /* no error, but no result either */
+ if (_dbus_string_get_length(socket_path) == 0)
+ {
+ return FALSE;
+ }
+
+ /* strip the carriage-return */
+ _dbus_string_shorten(socket_path, 1);
+ return TRUE;
+#else /* DBUS_ENABLE_LAUNCHD */
+ dbus_set_error(error, DBUS_ERROR_NOT_SUPPORTED,
+ "can't lookup socket from launchd; launchd support not compiled in");
+ return FALSE;
+#endif
+}
+
+#ifdef DBUS_ENABLE_LAUNCHD
+static dbus_bool_t
+_dbus_lookup_session_address_launchd (DBusString *address, DBusError *error)
+{
+ dbus_bool_t valid_socket;
+ DBusString socket_path;
+
+ if (_dbus_check_setuid ())
+ {
+ dbus_set_error_const (error, DBUS_ERROR_NOT_SUPPORTED,
+ "Unable to find launchd socket when setuid");
+ return FALSE;
+ }
+
+ if (!_dbus_string_init (&socket_path))
+ {
+ _DBUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ valid_socket = _dbus_lookup_launchd_socket (&socket_path, "DBUS_LAUNCHD_SESSION_BUS_SOCKET", error);
+
+ if (dbus_error_is_set(error))
+ {
+ _dbus_string_free(&socket_path);
+ return FALSE;
+ }
+
+ if (!valid_socket)
+ {
+ dbus_set_error(error, "no socket path",
+ "launchd did not provide a socket path, "
+ "verify that org.freedesktop.dbus-session.plist is loaded!");
+ _dbus_string_free(&socket_path);
+ return FALSE;
+ }
+ if (!_dbus_string_append (address, "unix:path="))
+ {
+ _DBUS_SET_OOM (error);
+ _dbus_string_free(&socket_path);
+ return FALSE;
+ }
+ if (!_dbus_string_copy (&socket_path, 0, address,
+ _dbus_string_get_length (address)))
+ {
+ _DBUS_SET_OOM (error);
+ _dbus_string_free(&socket_path);
+ return FALSE;
+ }
+
+ _dbus_string_free(&socket_path);
+ return TRUE;
+}
+#endif
+
+static dbus_bool_t
+_dbus_lookup_user_bus (dbus_bool_t *supported,
+ DBusString *address,
+ DBusError *error)
+{
+ const char *runtime_dir = _dbus_getenv ("XDG_RUNTIME_DIR");
+ dbus_bool_t ret = FALSE;
+ struct stat stbuf;
+ DBusString user_bus_path;
+
+ if (runtime_dir == NULL)
+ {
+ _dbus_verbose ("XDG_RUNTIME_DIR not found in environment");
+ *supported = FALSE;
+ return TRUE; /* Cannot use it, but not an error */
+ }
+
+ if (!_dbus_string_init (&user_bus_path))
+ {
+ _DBUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ if (!_dbus_string_append_printf (&user_bus_path, "%s/bus", runtime_dir))
+ {
+ _DBUS_SET_OOM (error);
+ goto out;
+ }
+
+ if (lstat (_dbus_string_get_const_data (&user_bus_path), &stbuf) == -1)
+ {
+ _dbus_verbose ("XDG_RUNTIME_DIR/bus not available: %s",
+ _dbus_strerror (errno));
+ *supported = FALSE;
+ ret = TRUE; /* Cannot use it, but not an error */
+ goto out;
+ }
+
+ if (stbuf.st_uid != getuid ())
+ {
+ _dbus_verbose ("XDG_RUNTIME_DIR/bus owned by uid %ld, not our uid %ld",
+ (long) stbuf.st_uid, (long) getuid ());
+ *supported = FALSE;
+ ret = TRUE; /* Cannot use it, but not an error */
+ goto out;
+ }
+
+ if ((stbuf.st_mode & S_IFMT) != S_IFSOCK)
+ {
+ _dbus_verbose ("XDG_RUNTIME_DIR/bus is not a socket: st_mode = 0o%lo",
+ (long) stbuf.st_mode);
+ *supported = FALSE;
+ ret = TRUE; /* Cannot use it, but not an error */
+ goto out;
+ }
+
+ if (!_dbus_string_append (address, "unix:path=") ||
+ !_dbus_address_append_escaped (address, &user_bus_path))
+ {
+ _DBUS_SET_OOM (error);
+ goto out;
+ }
+
+ *supported = TRUE;
+ ret = TRUE;
+
+out:
+ _dbus_string_free (&user_bus_path);
+ return ret;
+}
+
+/**
+ * Determines the address of the session bus by querying a
+ * platform-specific method.
+ *
+ * The first parameter will be a boolean specifying whether
+ * or not a dynamic session lookup is supported on this platform.
+ *
+ * If supported is TRUE and the return value is #TRUE, the
+ * address will be appended to @p address.
+ * If a failure happens, returns #FALSE and sets an error in
+ * @p error.
+ *
+ * If supported is FALSE, ignore the return value.
+ *
+ * @param supported returns whether this method is supported
+ * @param address a DBusString where the address can be stored
+ * @param error a DBusError to store the error in case of failure
+ * @returns #TRUE on success, #FALSE if an error happened
+ */
+dbus_bool_t
+_dbus_lookup_session_address (dbus_bool_t *supported,
+ DBusString *address,
+ DBusError *error)
+{
+#ifdef DBUS_ENABLE_LAUNCHD
+ *supported = TRUE;
+ return _dbus_lookup_session_address_launchd (address, error);
+#else
+ *supported = FALSE;
+
+ if (!_dbus_lookup_user_bus (supported, address, error))
+ return FALSE;
+ else if (*supported)
+ return TRUE;
+
+ /* On non-Mac Unix platforms, if the session address isn't already
+ * set in DBUS_SESSION_BUS_ADDRESS environment variable and the
+ * $XDG_RUNTIME_DIR/bus can't be used, we punt and fall back to the
+ * autolaunch: global default; see init_session_address in
+ * dbus/dbus-bus.c. */
+ return TRUE;
+#endif
+}
+
+/**
+ * Called when the bus daemon is signaled to reload its configuration; any
+ * caches should be nuked. Of course any caches that need explicit reload
+ * are probably broken, but c'est la vie.
+ *
+ *
+ */
+void
+_dbus_flush_caches (void)
+{
+ _dbus_user_database_flush_system ();
+}
+
+/**
+ * Appends the directory in which a keyring for the given credentials
+ * should be stored. The credentials should have either a Windows or
+ * UNIX user in them. The directory should be an absolute path.
+ *
+ * On UNIX the directory is ~/.dbus-keyrings while on Windows it should probably
+ * be something else, since the dotfile convention is not normal on Windows.
+ *
+ * @param directory string to append directory to
+ * @param credentials credentials the directory should be for
+ *
+ * @returns #FALSE on no memory
+ */
+dbus_bool_t
+_dbus_append_keyring_directory_for_credentials (DBusString *directory,
+ DBusCredentials *credentials)
+{
+ DBusString homedir;
+ DBusString dotdir;
+ dbus_uid_t uid;
+
+ _dbus_assert (credentials != NULL);
+ _dbus_assert (!_dbus_credentials_are_anonymous (credentials));
+
+ if (!_dbus_string_init (&homedir))
+ return FALSE;
+
+ uid = _dbus_credentials_get_unix_uid (credentials);
+ _dbus_assert (uid != DBUS_UID_UNSET);
+
+ if (!_dbus_homedir_from_uid (uid, &homedir))
+ goto failed;
+
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
+ {
+ const char *override;
+
+ override = _dbus_getenv ("DBUS_TEST_HOMEDIR");
+ if (override != NULL && *override != '\0')
+ {
+ _dbus_string_set_length (&homedir, 0);
+ if (!_dbus_string_append (&homedir, override))
+ goto failed;
+
+ _dbus_verbose ("Using fake homedir for testing: %s\n",
+ _dbus_string_get_const_data (&homedir));
+ }
+ else
+ {
+ /* Not strictly thread-safe, but if we fail at thread-safety here,
+ * the worst that will happen is some extra warnings. */
+ static dbus_bool_t already_warned = FALSE;
+ if (!already_warned)
+ {
+ _dbus_warn ("Using %s for testing, set DBUS_TEST_HOMEDIR to avoid",
+ _dbus_string_get_const_data (&homedir));
+ already_warned = TRUE;
+ }
+ }
+ }
+#endif
+
+ _dbus_string_init_const (&dotdir, ".dbus-keyrings");
+ if (!_dbus_concat_dir_and_file (&homedir,
+ &dotdir))
+ goto failed;
+
+ if (!_dbus_string_copy (&homedir, 0,
+ directory, _dbus_string_get_length (directory))) {
+ goto failed;
+ }
+
+ _dbus_string_free (&homedir);
+ return TRUE;
+
+ failed:
+ _dbus_string_free (&homedir);
+ return FALSE;
+}
+
+/* Documented in dbus-sysdeps-win.c, does nothing on Unix */
+dbus_bool_t
+_dbus_daemon_unpublish_session_bus_address (void)
+{
+ return TRUE;
+}
+
+/**
+ * See if errno is EAGAIN or EWOULDBLOCK (this has to be done differently
+ * for Winsock so is abstracted)
+ *
+ * @returns #TRUE if e == EAGAIN or e == EWOULDBLOCK
+ */
+dbus_bool_t
+_dbus_get_is_errno_eagain_or_ewouldblock (int e)
+{
+ /* Avoid the -Wlogical-op GCC warning, which can be triggered when EAGAIN and
+ * EWOULDBLOCK are numerically equal, which is permitted as described by
+ * errno(3).
+ */
+#if EAGAIN == EWOULDBLOCK
+ return e == EAGAIN;
+#else
+ return e == EAGAIN || e == EWOULDBLOCK;
+#endif
+}
+
+/**
+ * Removes a directory; Directory must be empty
+ *
+ * @param filename directory filename
+ * @param error initialized error object
+ * @returns #TRUE on success
+ */
+dbus_bool_t
+_dbus_delete_directory (const DBusString *filename,
+ DBusError *error)
+{
+ const char *filename_c;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ filename_c = _dbus_string_get_const_data (filename);
+
+ if (rmdir (filename_c) != 0)
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "Failed to remove directory %s: %s\n",
+ filename_c, _dbus_strerror (errno));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**
+ * Checks whether file descriptors may be passed via the socket
+ *
+ * @param fd the socket
+ * @return TRUE when fd passing over this socket is supported
+ *
+ */
+dbus_bool_t
+_dbus_socket_can_pass_unix_fd (DBusSocket fd)
+{
+#ifdef SCM_RIGHTS
+ union {
+ struct sockaddr sa;
+ struct sockaddr_storage storage;
+ struct sockaddr_un un;
+ } sa_buf;
+
+ socklen_t sa_len = sizeof(sa_buf);
+
+ _DBUS_ZERO(sa_buf);
+
+ if (getsockname(fd.fd, &sa_buf.sa, &sa_len) < 0)
+ return FALSE;
+
+ return sa_buf.sa.sa_family == AF_UNIX;
+
+#else
+ return FALSE;
+
+#endif
+}
+
+/*
+ * Similar to Solaris fdwalk(3), but without the ability to stop iteration,
+ * and may call func for integers that are not actually valid fds.
+ */
+static void
+act_on_fds_3_and_up (void (*func) (int fd))
+{
+ int maxfds, i;
+
+#if defined(__linux__) && defined(__GLIBC__)
+ DIR *d;
+
+ /* On Linux we can optimize this a bit if /proc is available. If it
+ isn't available, fall back to the brute force way. */
+
+ d = opendir ("/proc/self/fd");
+ if (d)
+ {
+ for (;;)
+ {
+ struct dirent *de;
+ int fd;
+ long l;
+ char *e = NULL;
+
+ de = readdir (d);
+ if (!de)
+ break;
+
+ if (de->d_name[0] == '.')
+ continue;
+
+ errno = 0;
+ l = strtol (de->d_name, &e, 10);
+ if (errno != 0 || e == NULL || *e != '\0')
+ continue;
+
+ fd = (int) l;
+ if (fd < 3)
+ continue;
+
+ if (fd == dirfd (d))
+ continue;
+
+ func (fd);
+ }
+
+ closedir (d);
+ return;
+ }
+#endif
+
+ maxfds = sysconf (_SC_OPEN_MAX);
+
+ /* Pick something reasonable if for some reason sysconf says
+ * unlimited.
+ */
+ if (maxfds < 0)
+ maxfds = 1024;
+
+ /* close all inherited fds */
+ for (i = 3; i < maxfds; i++)
+ func (i);
+}
+
+/* Some library implementations of closefrom() are not async-signal-safe,
+ * and we call _dbus_close_all() after forking, so we only do this on
+ * operating systems where we know that closefrom() is a system call */
+#if defined(HAVE_CLOSEFROM) && ( \
+ defined(__FreeBSD__) || \
+ defined(__NetBSD__) || \
+ defined(__OpenBSD__) || \
+ defined(__sun__) && defined(F_CLOSEFROM) \
+)
+#define CLOSEFROM_SIGNAL_SAFE 1
+#else
+#define CLOSEFROM_SIGNAL_SAFE 0
+static void
+close_ignore_error (int fd)
+{
+ close (fd);
+}
+#endif
+
+/**
+ * Closes all file descriptors except the first three (i.e. stdin,
+ * stdout, stderr).
+ */
+void
+_dbus_close_all (void)
+{
+#ifdef HAVE_CLOSE_RANGE
+ if (close_range (3, INT_MAX, 0) == 0)
+ return;
+#endif
+
+#if CLOSEFROM_SIGNAL_SAFE
+ closefrom (3);
+#else
+ act_on_fds_3_and_up (close_ignore_error);
+#endif
+}
+
+/**
+ * Sets all file descriptors except the first three (i.e. stdin,
+ * stdout, stderr) to be close-on-execute.
+ */
+void
+_dbus_fd_set_all_close_on_exec (void)
+{
+#if defined(HAVE_CLOSE_RANGE) && defined(CLOSE_RANGE_CLOEXEC)
+ if (close_range (3, INT_MAX, CLOSE_RANGE_CLOEXEC) == 0)
+ return;
+#endif
+
+ act_on_fds_3_and_up (_dbus_fd_set_close_on_exec);
+}
+
+/**
+ * **NOTE**: If you modify this function, please also consider making
+ * the corresponding change in GLib. See
+ * glib/gutils.c:g_check_setuid().
+ *
+ * Returns TRUE if the current process was executed as setuid (or an
+ * equivalent __libc_enable_secure is available). See:
+ * http://osdir.com/ml/linux.lfs.hardened/2007-04/msg00032.html
+ */
+dbus_bool_t
+_dbus_check_setuid (void)
+{
+ /* TODO: get __libc_enable_secure exported from glibc.
+ * See http://www.openwall.com/lists/owl-dev/2012/08/14/1
+ */
+#if 0 && defined(HAVE_LIBC_ENABLE_SECURE)
+ {
+ /* See glibc/include/unistd.h */
+ extern int __libc_enable_secure;
+ return __libc_enable_secure;
+ }
+#elif defined(HAVE_ISSETUGID)
+ /* BSD: http://www.freebsd.org/cgi/man.cgi?query=issetugid&sektion=2 */
+ return issetugid ();
+#else
+ uid_t ruid, euid, suid; /* Real, effective and saved user ID's */
+ gid_t rgid, egid, sgid; /* Real, effective and saved group ID's */
+
+ /* We call into this function from _dbus_threads_init_platform_specific()
+ * to make sure these are initialized before we start threading. */
+ static dbus_bool_t check_setuid_initialised;
+ static dbus_bool_t is_setuid;
+
+ if (_DBUS_UNLIKELY (!check_setuid_initialised))
+ {
+#ifdef HAVE_GETRESUID
+ if (getresuid (&ruid, &euid, &suid) != 0 ||
+ getresgid (&rgid, &egid, &sgid) != 0)
+#endif /* HAVE_GETRESUID */
+ {
+ suid = ruid = getuid ();
+ sgid = rgid = getgid ();
+ euid = geteuid ();
+ egid = getegid ();
+ }
+
+ check_setuid_initialised = TRUE;
+ is_setuid = (ruid != euid || ruid != suid ||
+ rgid != egid || rgid != sgid);
+
+ }
+ return is_setuid;
+#endif
+}
+
+/**
+ * Read the address from the socket and append it to the string
+ *
+ * @param fd the socket
+ * @param address
+ * @param error return location for error code
+ */
+dbus_bool_t
+_dbus_append_address_from_socket (DBusSocket fd,
+ DBusString *address,
+ DBusError *error)
+{
+ union {
+ struct sockaddr sa;
+ struct sockaddr_storage storage;
+ struct sockaddr_un un;
+ struct sockaddr_in ipv4;
+ struct sockaddr_in6 ipv6;
+ } socket;
+ char hostip[INET6_ADDRSTRLEN];
+ socklen_t size = sizeof (socket);
+ DBusString path_str;
+ const char *family_name = NULL;
+ dbus_uint16_t port;
+
+ if (getsockname (fd.fd, &socket.sa, &size))
+ goto err;
+
+ switch (socket.sa.sa_family)
+ {
+ case AF_UNIX:
+ if (socket.un.sun_path[0]=='\0')
+ {
+ _dbus_string_init_const (&path_str, &(socket.un.sun_path[1]));
+ if (_dbus_string_append (address, "unix:abstract=") &&
+ _dbus_address_append_escaped (address, &path_str))
+ {
+ return TRUE;
+ }
+ else
+ {
+ _DBUS_SET_OOM (error);
+ return FALSE;
+ }
+ }
+ else
+ {
+ _dbus_string_init_const (&path_str, socket.un.sun_path);
+ if (_dbus_string_append (address, "unix:path=") &&
+ _dbus_address_append_escaped (address, &path_str))
+ {
+ return TRUE;
+ }
+ else
+ {
+ _DBUS_SET_OOM (error);
+ return FALSE;
+ }
+ }
+ /* not reached */
+ break;
+
+ case AF_INET:
+#ifdef AF_INET6
+ case AF_INET6:
+#endif
+ _dbus_string_init_const (&path_str, hostip);
+
+ if (_dbus_inet_sockaddr_to_string (&socket, size, hostip, sizeof (hostip),
+ &family_name, &port, error))
+ {
+ if (_dbus_string_append_printf (address, "tcp:family=%s,port=%u,host=",
+ family_name, port) &&
+ _dbus_address_append_escaped (address, &path_str))
+ {
+ return TRUE;
+ }
+ else
+ {
+ _DBUS_SET_OOM (error);
+ return FALSE;
+ }
+ }
+ else
+ {
+ return FALSE;
+ }
+ /* not reached */
+ break;
+
+ default:
+ dbus_set_error (error,
+ _dbus_error_from_errno (EINVAL),
+ "Failed to read address from socket: Unknown socket type.");
+ return FALSE;
+ }
+ err:
+ dbus_set_error (error,
+ _dbus_error_from_errno (errno),
+ "Failed to read address from socket: %s",
+ _dbus_strerror (errno));
+ return FALSE;
+}
+
+int
+_dbus_save_socket_errno (void)
+{
+ return errno;
+}
+
+void
+_dbus_restore_socket_errno (int saved_errno)
+{
+ errno = saved_errno;
+}
+
+static const char *syslog_tag = "dbus";
+#ifdef HAVE_SYSLOG_H
+static DBusLogFlags log_flags = DBUS_LOG_FLAGS_STDERR;
+#endif
+
+/**
+ * Initialize the system log.
+ *
+ * The "tag" is not copied, and must remain valid for the entire lifetime of
+ * the process or until _dbus_init_system_log() is called again. In practice
+ * it will normally be a constant.
+ *
+ * On platforms that do not support a system log, the
+ * #DBUS_LOG_FLAGS_SYSTEM_LOG flag is treated as equivalent to
+ * #DBUS_LOG_FLAGS_STDERR.
+ *
+ * @param tag the name of the executable (syslog tag)
+ * @param mode whether to log to stderr, the system log or both
+ */
+void
+_dbus_init_system_log (const char *tag,
+ DBusLogFlags flags)
+{
+ /* We never want to turn off logging completely */
+ _dbus_assert (
+ (flags & (DBUS_LOG_FLAGS_STDERR | DBUS_LOG_FLAGS_SYSTEM_LOG)) != 0);
+
+ syslog_tag = tag;
+
+#ifdef HAVE_SYSLOG_H
+ log_flags = flags;
+
+ if (log_flags & DBUS_LOG_FLAGS_SYSTEM_LOG)
+ openlog (tag, LOG_PID, LOG_DAEMON);
+#endif
+}
+
+/**
+ * Log a message to the system log file (e.g. syslog on Unix) and/or stderr.
+ *
+ * @param severity a severity value
+ * @param msg a printf-style format string
+ * @param args arguments for the format string
+ */
+void
+_dbus_logv (DBusSystemLogSeverity severity,
+ const char *msg,
+ va_list args)
+{
+ va_list tmp;
+#ifdef HAVE_SYSLOG_H
+ if (log_flags & DBUS_LOG_FLAGS_SYSTEM_LOG)
+ {
+ int flags;
+ switch (severity)
+ {
+ case DBUS_SYSTEM_LOG_INFO:
+ flags = LOG_DAEMON | LOG_INFO;
+ break;
+ case DBUS_SYSTEM_LOG_WARNING:
+ flags = LOG_DAEMON | LOG_WARNING;
+ break;
+ case DBUS_SYSTEM_LOG_SECURITY:
+ flags = LOG_AUTH | LOG_NOTICE;
+ break;
+ case DBUS_SYSTEM_LOG_ERROR:
+ flags = LOG_DAEMON|LOG_CRIT;
+ break;
+ default:
+ _dbus_assert_not_reached ("invalid log severity");
+ }
+
+ va_copy (tmp, args);
+ vsyslog (flags, msg, tmp);
+ va_end (tmp);
+ }
+
+ /* If we don't have syslog.h, we always behave as though stderr was in
+ * the flags */
+ if (log_flags & DBUS_LOG_FLAGS_STDERR)
+#endif
+ {
+ va_copy (tmp, args);
+ fprintf (stderr, "%s[" DBUS_PID_FORMAT "]: ", syslog_tag, _dbus_getpid ());
+ vfprintf (stderr, msg, tmp);
+ fputc ('\n', stderr);
+ va_end (tmp);
+ }
+}
+
+/*
+ * Return the low-level representation of a socket error, as used by
+ * cross-platform socket APIs like inet_ntop(), send() and recv(). This
+ * is the standard errno on Unix, but is WSAGetLastError() on Windows.
+ *
+ * Some libdbus internal functions copy this into errno, but with
+ * hindsight that was probably a design flaw.
+ */
+int
+_dbus_get_low_level_socket_errno (void)
+{
+ return errno;
+}
+
+/* tests in dbus-sysdeps-util.c */
diff --git a/src/3rdparty/libdbus/dbus/dbus-sysdeps-unix.h b/src/3rdparty/libdbus/dbus/dbus-sysdeps-unix.h
new file mode 100644
index 00000000..b400cf86
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-sysdeps-unix.h
@@ -0,0 +1,169 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-sysdeps-unix.h UNIX-specific wrappers around system/libc features (internal to D-Bus implementation)
+ *
+ * Copyright (C) 2002, 2003, 2006 Red Hat, Inc.
+ * Copyright (C) 2003 CodeFactory AB
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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
+ *
+ */
+
+#ifndef DBUS_SYSDEPS_UNIX_H
+#define DBUS_SYSDEPS_UNIX_H
+
+#include <dbus/dbus-sysdeps.h>
+
+#ifdef DBUS_WIN
+#error "Don't include this on Windows"
+#endif
+
+DBUS_BEGIN_DECLS
+
+/**
+ * @defgroup DBusSysdepsUnix UNIX-specific internal API
+ * @ingroup DBusInternals
+ * @brief Internal system-dependent API available on UNIX only
+ * @{
+ */
+
+DBUS_PRIVATE_EXPORT
+dbus_bool_t
+_dbus_close (int fd,
+ DBusError *error);
+DBUS_PRIVATE_EXPORT
+int _dbus_dup (int fd,
+ DBusError *error);
+DBUS_PRIVATE_EXPORT
+int
+_dbus_read (int fd,
+ DBusString *buffer,
+ int count);
+int
+_dbus_write (int fd,
+ const DBusString *buffer,
+ int start,
+ int len);
+int
+_dbus_write_two (int fd,
+ const DBusString *buffer1,
+ int start1,
+ int len1,
+ const DBusString *buffer2,
+ int start2,
+ int len2);
+
+int _dbus_listen_systemd_sockets (DBusSocket **fd,
+ DBusError *error);
+
+dbus_bool_t _dbus_read_credentials (int client_fd,
+ DBusCredentials *credentials,
+ DBusError *error);
+dbus_bool_t _dbus_send_credentials (int server_fd,
+ DBusError *error);
+
+dbus_bool_t _dbus_lookup_launchd_socket (DBusString *socket_path,
+ const char *launchd_env_var,
+ DBusError *error);
+
+/** Information about a UNIX user */
+typedef struct DBusUserInfo DBusUserInfo;
+/** Information about a UNIX group */
+typedef struct DBusGroupInfo DBusGroupInfo;
+
+/**
+ * Information about a UNIX user
+ */
+struct DBusUserInfo
+{
+ size_t refcount; /**< Reference count */
+ dbus_uid_t uid; /**< UID */
+ dbus_gid_t primary_gid; /**< GID */
+ dbus_gid_t *group_ids; /**< Groups IDs, *including* above primary group */
+ int n_group_ids; /**< Size of group IDs array */
+ char *username; /**< Username */
+ char *homedir; /**< Home directory */
+};
+
+/**
+ * Information about a UNIX group
+ */
+struct DBusGroupInfo
+{
+ size_t refcount; /**< Reference count */
+ dbus_gid_t gid; /**< GID */
+ char *groupname; /**< Group name */
+};
+
+dbus_bool_t _dbus_user_info_fill (DBusUserInfo *info,
+ const DBusString *username,
+ DBusError *error);
+dbus_bool_t _dbus_user_info_fill_uid (DBusUserInfo *info,
+ dbus_uid_t uid,
+ DBusError *error);
+void _dbus_user_info_free (DBusUserInfo *info);
+
+dbus_bool_t _dbus_group_info_fill (DBusGroupInfo *info,
+ const DBusString *groupname,
+ DBusError *error);
+dbus_bool_t _dbus_group_info_fill_gid (DBusGroupInfo *info,
+ dbus_gid_t gid,
+ DBusError *error);
+void _dbus_group_info_free (DBusGroupInfo *info);
+
+DBUS_PRIVATE_EXPORT
+dbus_uid_t _dbus_geteuid (void);
+
+DBUS_PRIVATE_EXPORT
+void _dbus_close_all (void);
+DBUS_PRIVATE_EXPORT
+void _dbus_fd_set_all_close_on_exec (void);
+DBUS_PRIVATE_EXPORT
+void _dbus_fd_clear_close_on_exec (int fd);
+
+dbus_bool_t _dbus_append_address_from_socket (DBusSocket fd,
+ DBusString *address,
+ DBusError *error);
+
+DBUS_PRIVATE_EXPORT
+void _dbus_fd_set_close_on_exec (int fd);
+
+typedef enum
+{
+ DBUS_FORCE_STDIN_NULL = (1 << 0),
+ DBUS_FORCE_STDOUT_NULL = (1 << 1),
+ DBUS_FORCE_STDERR_NULL = (1 << 2)
+} DBusEnsureStandardFdsFlags;
+
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_ensure_standard_fds (DBusEnsureStandardFdsFlags flags,
+ const char **error_str_p);
+
+/** A UNIX signal handler */
+typedef void (* DBusSignalHandler) (int sig);
+
+void _dbus_set_signal_handler (int sig,
+ DBusSignalHandler handler);
+
+dbus_bool_t _dbus_reset_oom_score_adj (const char **error_str_p);
+
+/** @} */
+
+DBUS_END_DECLS
+
+#endif /* DBUS_SYSDEPS_UNIX_H */
diff --git a/src/3rdparty/libdbus/dbus/dbus-sysdeps-win.c b/src/3rdparty/libdbus/dbus/dbus-sysdeps-win.c
new file mode 100644
index 00000000..d44dc0a9
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-sysdeps-win.c
@@ -0,0 +1,4521 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-sysdeps.c Wrappers around system/libc features (internal to D-BUS implementation)
+ *
+ * Copyright (C) 2002, 2003 Red Hat, Inc.
+ * Copyright (C) 2003 CodeFactory AB
+ * Copyright (C) 2005 Novell, Inc.
+ * Copyright (C) 2006 Peter Kümmel <syntheticpp@gmx.net>
+ * Copyright (C) 2006 Christian Ehrlicher <ch.ehrlicher@gmx.de>
+ * Copyright (C) 2006-2021 Ralf Habacker <ralf.habacker@freenet.de>
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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>
+
+#define STRSAFE_NO_DEPRECATE
+
+#include "dbus-internals.h"
+#include "dbus-sha.h"
+#include "dbus-sysdeps.h"
+#include "dbus-threads.h"
+#include "dbus-protocol.h"
+#include "dbus-string.h"
+#include "dbus-sysdeps.h"
+#include "dbus-sysdeps-win.h"
+#include "dbus-protocol.h"
+#include "dbus-hash.h"
+#include "dbus-sockets-win.h"
+#include "dbus-list.h"
+#include "dbus-nonce.h"
+#include "dbus-credentials.h"
+
+#include <windows.h>
+#include <wincrypt.h>
+#include <iphlpapi.h>
+#ifdef HAVE_AFUNIX_H
+#include <afunix.h>
+#endif
+
+/* Declarations missing in mingw's and windows sdk 7.0 headers */
+extern BOOL WINAPI ConvertStringSidToSidA (LPCSTR StringSid, PSID *Sid);
+extern BOOL WINAPI ConvertSidToStringSidA (PSID Sid, LPSTR *StringSid);
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <string.h>
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifndef DBUS_WINCE
+#include <mbstring.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#endif
+
+#ifdef HAVE_WS2TCPIP_H
+/* getaddrinfo for Windows CE (and Windows). */
+#include <ws2tcpip.h>
+#endif
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+#ifndef PROCESS_QUERY_LIMITED_INFORMATION
+/* MinGW32 < 4 does not define this value in its headers */
+#define PROCESS_QUERY_LIMITED_INFORMATION (0x1000)
+#endif
+
+typedef int socklen_t;
+
+/* uncomment to enable windows event based poll implementation */
+//#define USE_CHRIS_IMPL
+
+void
+_dbus_win_set_errno (int err)
+{
+#ifdef DBUS_WINCE
+ SetLastError (err);
+#else
+ errno = err;
+#endif
+}
+
+static BOOL is_winxp_sp3_or_lower (void);
+
+/*
+ * _MIB_TCPROW_EX and friends are not available in system headers
+ * and are mapped to attribute identical ...OWNER_PID typedefs.
+ */
+typedef MIB_TCPROW_OWNER_PID _MIB_TCPROW_EX;
+typedef MIB_TCPTABLE_OWNER_PID MIB_TCPTABLE_EX;
+typedef PMIB_TCPTABLE_OWNER_PID PMIB_TCPTABLE_EX;
+typedef DWORD (WINAPI *ProcAllocateAndGetTcpExtTableFromStack)(PMIB_TCPTABLE_EX*,BOOL,HANDLE,DWORD,DWORD);
+
+/* Not protected by a lock, but if we miss a write, all that
+ * happens is that the lazy initialization will happen in two threads
+ * concurrently - it results in the same value either way so that's OK */
+static ProcAllocateAndGetTcpExtTableFromStack lpfnAllocateAndGetTcpExTableFromStack = NULL;
+
+/**
+ * AllocateAndGetTcpExTableFromStack() is undocumented and not exported,
+ * but is the only way to do this in older XP versions.
+ * @return true if the procedures could be loaded
+ */
+static BOOL
+load_ex_ip_helper_procedures(void)
+{
+ HMODULE hModule = LoadLibrary ("iphlpapi.dll");
+ if (hModule == NULL)
+ {
+ _dbus_verbose ("could not load iphlpapi.dll\n");
+ return FALSE;
+ }
+
+ lpfnAllocateAndGetTcpExTableFromStack = (ProcAllocateAndGetTcpExtTableFromStack) (void (*)(void))GetProcAddress (hModule, "AllocateAndGetTcpExTableFromStack");
+ if (lpfnAllocateAndGetTcpExTableFromStack == NULL)
+ {
+ _dbus_verbose ("could not find function AllocateAndGetTcpExTableFromStack in iphlpapi.dll\n");
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/**
+ * get pid from localhost tcp connection using peer_port
+ * This function is available on WinXP >= SP3
+ * @param peer_port peers tcp port
+ * @return process id or 0 if connection has not been found
+ */
+static dbus_pid_t
+get_pid_from_extended_tcp_table(int peer_port)
+{
+ dbus_pid_t result;
+ DWORD errorCode, size = 0, i;
+ MIB_TCPTABLE_OWNER_PID *tcp_table;
+
+ if ((errorCode =
+ GetExtendedTcpTable (NULL, &size, TRUE, AF_INET, TCP_TABLE_OWNER_PID_ALL, 0)) == ERROR_INSUFFICIENT_BUFFER)
+ {
+ tcp_table = (MIB_TCPTABLE_OWNER_PID *) dbus_malloc (size);
+ if (tcp_table == NULL)
+ {
+ _dbus_verbose ("Error allocating memory\n");
+ return 0;
+ }
+ }
+ else
+ {
+ _dbus_win_warn_win_error ("unexpected error returned from GetExtendedTcpTable", errorCode);
+ return 0;
+ }
+
+ if ((errorCode = GetExtendedTcpTable (tcp_table, &size, TRUE, AF_INET, TCP_TABLE_OWNER_PID_ALL, 0)) != NO_ERROR)
+ {
+ _dbus_verbose ("Error fetching tcp table %d\n", (int)errorCode);
+ dbus_free (tcp_table);
+ return 0;
+ }
+
+ result = 0;
+ for (i = 0; i < tcp_table->dwNumEntries; i++)
+ {
+ MIB_TCPROW_OWNER_PID *p = &tcp_table->table[i];
+ int local_address = ntohl (p->dwLocalAddr);
+ int local_port = ntohs (p->dwLocalPort);
+ if (p->dwState == MIB_TCP_STATE_ESTAB
+ && local_address == INADDR_LOOPBACK && local_port == peer_port)
+ result = p->dwOwningPid;
+ }
+
+ dbus_free (tcp_table);
+ _dbus_verbose ("got pid %lu\n", result);
+ return result;
+}
+
+/**
+ * get pid from localhost tcp connection using peer_port
+ * This function is available on all WinXP versions, but
+ * not in wine (at least version <= 1.6.0)
+ * @param peer_port peers tcp port
+ * @return process id or 0 if connection has not been found
+ */
+static dbus_pid_t
+get_pid_from_tcp_ex_table(int peer_port)
+{
+ dbus_pid_t result;
+ DWORD errorCode, i;
+ PMIB_TCPTABLE_EX tcp_table = NULL;
+
+ if (!load_ex_ip_helper_procedures ())
+ {
+ _dbus_verbose
+ ("Error not been able to load iphelper procedures\n");
+ return 0;
+ }
+
+ errorCode = lpfnAllocateAndGetTcpExTableFromStack (&tcp_table, TRUE, GetProcessHeap(), 0, 2);
+
+ if (errorCode != NO_ERROR)
+ {
+ _dbus_verbose
+ ("Error not been able to call AllocateAndGetTcpExTableFromStack()\n");
+ return 0;
+ }
+
+ result = 0;
+ for (i = 0; i < tcp_table->dwNumEntries; i++)
+ {
+ _MIB_TCPROW_EX *p = &tcp_table->table[i];
+ int local_port = ntohs (p->dwLocalPort);
+ int local_address = ntohl (p->dwLocalAddr);
+ if (local_address == INADDR_LOOPBACK && local_port == peer_port)
+ {
+ result = p->dwOwningPid;
+ break;
+ }
+ }
+
+ HeapFree (GetProcessHeap(), 0, tcp_table);
+ _dbus_verbose ("got pid %lu\n", result);
+ return result;
+}
+
+/**
+ * @brief return peer process id from tcp handle for localhost connections
+ * @param handle tcp socket descriptor
+ * @return process id or 0 in case the process id could not be fetched
+ */
+static dbus_pid_t
+_dbus_get_peer_pid_from_tcp_handle (int handle)
+{
+ struct sockaddr_storage addr;
+ socklen_t len = sizeof (addr);
+ int peer_port;
+
+ dbus_pid_t result;
+ dbus_bool_t is_localhost = FALSE;
+
+ getpeername (handle, (struct sockaddr *) &addr, &len);
+
+ if (addr.ss_family == AF_INET)
+ {
+ struct sockaddr_in *s = (struct sockaddr_in *) &addr;
+ peer_port = ntohs (s->sin_port);
+ is_localhost = (ntohl (s->sin_addr.s_addr) == INADDR_LOOPBACK);
+ }
+ else if (addr.ss_family == AF_INET6)
+ {
+ _dbus_verbose ("FIXME [61922]: IPV6 support not working on windows\n");
+ return 0;
+ /*
+ struct sockaddr_in6 *s = (struct sockaddr_in6 * )&addr;
+ peer_port = ntohs (s->sin6_port);
+ is_localhost = (memcmp(s->sin6_addr.s6_addr, in6addr_loopback.s6_addr, 16) == 0);
+ _dbus_verbose ("IPV6 %08x %08x\n", s->sin6_addr.s6_addr, in6addr_loopback.s6_addr);
+ */
+ }
+ else
+ {
+ _dbus_verbose ("no idea what address family %d is\n", addr.ss_family);
+ return 0;
+ }
+
+ if (!is_localhost)
+ {
+ _dbus_verbose ("could not fetch process id from remote process\n");
+ return 0;
+ }
+
+ if (peer_port == 0)
+ {
+ _dbus_verbose
+ ("Error not been able to fetch tcp peer port from connection\n");
+ return 0;
+ }
+
+ _dbus_verbose ("trying to get peer's pid\n");
+
+ result = get_pid_from_extended_tcp_table (peer_port);
+ if (result > 0)
+ return result;
+ result = get_pid_from_tcp_ex_table (peer_port);
+ return result;
+}
+
+/* Convert GetLastError() to a dbus error. */
+const char*
+_dbus_win_error_from_last_error (void)
+{
+ switch (GetLastError())
+ {
+ case 0:
+ return DBUS_ERROR_FAILED;
+
+ case ERROR_NO_MORE_FILES:
+ case ERROR_TOO_MANY_OPEN_FILES:
+ return DBUS_ERROR_LIMITS_EXCEEDED; /* kernel out of memory */
+
+ case ERROR_ACCESS_DENIED:
+ case ERROR_CANNOT_MAKE:
+ return DBUS_ERROR_ACCESS_DENIED;
+
+ case ERROR_NOT_ENOUGH_MEMORY:
+ return DBUS_ERROR_NO_MEMORY;
+
+ case ERROR_FILE_EXISTS:
+ return DBUS_ERROR_FILE_EXISTS;
+
+ case ERROR_FILE_NOT_FOUND:
+ case ERROR_PATH_NOT_FOUND:
+ return DBUS_ERROR_FILE_NOT_FOUND;
+
+ default:
+ return DBUS_ERROR_FAILED;
+ }
+}
+
+
+char*
+_dbus_win_error_string (int error_number)
+{
+ char *msg;
+
+ FormatMessageA (FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_IGNORE_INSERTS |
+ FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL, error_number, 0,
+ (LPSTR) &msg, 0, NULL);
+
+ if (msg[strlen (msg) - 1] == '\n')
+ msg[strlen (msg) - 1] = '\0';
+ if (msg[strlen (msg) - 1] == '\r')
+ msg[strlen (msg) - 1] = '\0';
+
+ return msg;
+}
+
+void
+_dbus_win_free_error_string (char *string)
+{
+ LocalFree (string);
+}
+
+/**
+ * Socket interface
+ *
+ */
+
+/**
+ * Thin wrapper around the read() system call that appends
+ * the data it reads to the DBusString buffer. It appends
+ * up to the given count, and returns the same value
+ * and same errno as read(). The only exception is that
+ * _dbus_read_socket() handles EINTR for you.
+ * _dbus_read_socket() can return ENOMEM, even though
+ * regular UNIX read doesn't.
+ *
+ * @param fd the file descriptor to read from
+ * @param buffer the buffer to append data to
+ * @param count the amount of data to read
+ * @returns the number of bytes read or -1
+ */
+
+int
+_dbus_read_socket (DBusSocket fd,
+ DBusString *buffer,
+ int count)
+{
+ int bytes_read;
+ int start;
+ char *data;
+
+ _dbus_assert (count >= 0);
+
+ start = _dbus_string_get_length (buffer);
+
+ if (!_dbus_string_lengthen (buffer, count))
+ {
+ _dbus_win_set_errno (ENOMEM);
+ return -1;
+ }
+
+ data = _dbus_string_get_data_len (buffer, start, count);
+
+ again:
+
+ _dbus_verbose ("recv: count=%d fd=%Iu\n", count, fd.sock);
+ bytes_read = recv (fd.sock, data, count, 0);
+
+ if (bytes_read == SOCKET_ERROR)
+ {
+ DBUS_SOCKET_SET_ERRNO();
+ _dbus_verbose ("recv: failed: %s (%d)\n", _dbus_strerror (errno), errno);
+ bytes_read = -1;
+ }
+ else
+ _dbus_verbose ("recv: = %d\n", bytes_read);
+
+ if (bytes_read < 0)
+ {
+ if (errno == EINTR)
+ goto again;
+ else
+ {
+ /* put length back (note that this doesn't actually realloc anything) */
+ _dbus_string_set_length (buffer, start);
+ return -1;
+ }
+ }
+ else
+ {
+ /* put length back (doesn't actually realloc) */
+ _dbus_string_set_length (buffer, start + bytes_read);
+
+#if 0
+ if (bytes_read > 0)
+ _dbus_verbose_bytes_of_string (buffer, start, bytes_read);
+#endif
+
+ return bytes_read;
+ }
+}
+
+/**
+ * Thin wrapper around the write() system call that writes a part of a
+ * DBusString and handles EINTR for you.
+ *
+ * @param fd the file descriptor to write
+ * @param buffer the buffer to write data from
+ * @param start the first byte in the buffer to write
+ * @param len the number of bytes to try to write
+ * @returns the number of bytes written or -1 on error
+ */
+int
+_dbus_write_socket (DBusSocket fd,
+ const DBusString *buffer,
+ int start,
+ int len)
+{
+ const char *data;
+ int bytes_written;
+
+ data = _dbus_string_get_const_data_len (buffer, start, len);
+
+ again:
+
+ _dbus_verbose ("send: len=%d fd=%Iu\n", len, fd.sock);
+ bytes_written = send (fd.sock, data, len, 0);
+
+ if (bytes_written == SOCKET_ERROR)
+ {
+ DBUS_SOCKET_SET_ERRNO();
+ _dbus_verbose ("send: failed: %s\n", _dbus_strerror_from_errno ());
+ bytes_written = -1;
+ }
+ else
+ _dbus_verbose ("send: = %d\n", bytes_written);
+
+ if (bytes_written < 0 && errno == EINTR)
+ goto again;
+
+#if 0
+ if (bytes_written > 0)
+ _dbus_verbose_bytes_of_string (buffer, start, bytes_written);
+#endif
+
+ return bytes_written;
+}
+
+
+/**
+ * Closes a socket and invalidates it.
+ *
+ * @param fd the file descriptor
+ * @param error error object
+ * @returns #FALSE if error set
+ */
+dbus_bool_t
+_dbus_close_socket (DBusSocket *fd,
+ DBusError *error)
+{
+ _dbus_assert (fd != NULL);
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ again:
+ if (closesocket (fd->sock) == SOCKET_ERROR)
+ {
+ DBUS_SOCKET_SET_ERRNO ();
+
+ if (errno == EINTR)
+ goto again;
+
+ dbus_set_error (error, _dbus_error_from_errno (errno),
+ "Could not close socket: socket=%Iu, , %s",
+ fd->sock, _dbus_strerror_from_errno ());
+ _dbus_socket_invalidate (fd);
+ return FALSE;
+ }
+ _dbus_verbose ("socket=%Iu, \n", fd->sock);
+
+ _dbus_socket_invalidate (fd);
+ return TRUE;
+}
+
+/**
+ * Sets the file descriptor to be close
+ * on exec. Should be called for all file
+ * descriptors in D-Bus code.
+ *
+ * @param handle the Windows HANDLE (a SOCKET is also OK)
+ */
+static void
+_dbus_win_handle_set_close_on_exec (HANDLE handle)
+{
+ if ( !SetHandleInformation( (HANDLE) handle,
+ HANDLE_FLAG_INHERIT | HANDLE_FLAG_PROTECT_FROM_CLOSE,
+ 0 /*disable both flags*/ ) )
+ {
+ _dbus_win_warn_win_error ("Disabling socket handle inheritance failed:", GetLastError());
+ }
+}
+
+/**
+ * Sets a file descriptor to be nonblocking.
+ *
+ * @param handle the file descriptor.
+ * @param error address of error location.
+ * @returns #TRUE on success.
+ */
+dbus_bool_t
+_dbus_set_socket_nonblocking (DBusSocket handle,
+ DBusError *error)
+{
+ u_long one = 1;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ if (ioctlsocket (handle.sock, FIONBIO, &one) == SOCKET_ERROR)
+ {
+ DBUS_SOCKET_SET_ERRNO ();
+ dbus_set_error (error, _dbus_error_from_errno (errno),
+ "Failed to set socket %Iu to nonblocking: %s",
+ handle.sock, _dbus_strerror_from_errno ());
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+/**
+ * Like _dbus_write() but will use writev() if possible
+ * to write both buffers in sequence. The return value
+ * is the number of bytes written in the first buffer,
+ * plus the number written in the second. If the first
+ * buffer is written successfully and an error occurs
+ * writing the second, the number of bytes in the first
+ * is returned (i.e. the error is ignored), on systems that
+ * don't have writev. Handles EINTR for you.
+ * The second buffer may be #NULL.
+ *
+ * @param fd the file descriptor
+ * @param buffer1 first buffer
+ * @param start1 first byte to write in first buffer
+ * @param len1 number of bytes to write from first buffer
+ * @param buffer2 second buffer, or #NULL
+ * @param start2 first byte to write in second buffer
+ * @param len2 number of bytes to write in second buffer
+ * @returns total bytes written from both buffers, or -1 on error
+ */
+int
+_dbus_write_socket_two (DBusSocket fd,
+ const DBusString *buffer1,
+ int start1,
+ int len1,
+ const DBusString *buffer2,
+ int start2,
+ int len2)
+{
+ WSABUF vectors[2];
+ const char *data1;
+ const char *data2;
+ int rc;
+ DWORD bytes_written;
+
+ _dbus_assert (buffer1 != NULL);
+ _dbus_assert (start1 >= 0);
+ _dbus_assert (start2 >= 0);
+ _dbus_assert (len1 >= 0);
+ _dbus_assert (len2 >= 0);
+
+
+ data1 = _dbus_string_get_const_data_len (buffer1, start1, len1);
+
+ if (buffer2 != NULL)
+ data2 = _dbus_string_get_const_data_len (buffer2, start2, len2);
+ else
+ {
+ data2 = NULL;
+ start2 = 0;
+ len2 = 0;
+ }
+
+ vectors[0].buf = (char*) data1;
+ vectors[0].len = len1;
+ vectors[1].buf = (char*) data2;
+ vectors[1].len = len2;
+
+ again:
+
+ _dbus_verbose ("WSASend: len1+2=%d+%d fd=%Iu\n", len1, len2, fd.sock);
+ rc = WSASend (fd.sock,
+ vectors,
+ data2 ? 2 : 1,
+ &bytes_written,
+ 0,
+ NULL,
+ NULL);
+
+ if (rc == SOCKET_ERROR)
+ {
+ DBUS_SOCKET_SET_ERRNO ();
+ _dbus_verbose ("WSASend: failed: %s\n", _dbus_strerror_from_errno ());
+ bytes_written = (DWORD) -1;
+ }
+ else
+ _dbus_verbose ("WSASend: = %ld\n", bytes_written);
+
+ if (bytes_written == (DWORD) -1 && errno == EINTR)
+ goto again;
+
+ return bytes_written;
+}
+
+#if 0
+
+/**
+ * Opens the client side of a Windows named pipe. The connection D-BUS
+ * file descriptor index is returned. It is set up as nonblocking.
+ *
+ * @param path the path to named pipe socket
+ * @param error return location for error code
+ * @returns connection D-BUS file descriptor or -1 on error
+ */
+int
+_dbus_connect_named_pipe (const char *path,
+ DBusError *error)
+{
+ _dbus_assert_not_reached ("not implemented");
+}
+
+#endif
+
+/**
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_win_startup_winsock (void)
+{
+ /* Straight from MSDN, deuglified */
+
+ /* Protected by _DBUS_LOCK_sysdeps */
+ static dbus_bool_t beenhere = FALSE;
+
+ WORD wVersionRequested;
+ WSADATA wsaData;
+ int err;
+
+ if (!_DBUS_LOCK (sysdeps))
+ return FALSE;
+
+ if (beenhere)
+ goto out;
+
+ wVersionRequested = MAKEWORD (2, 0);
+
+ err = WSAStartup (wVersionRequested, &wsaData);
+ if (err != 0)
+ {
+ _dbus_assert_not_reached ("Could not initialize WinSock");
+ _dbus_abort ();
+ }
+
+ /* Confirm that the WinSock DLL supports 2.0. Note that if the DLL
+ * supports versions greater than 2.0 in addition to 2.0, it will
+ * still return 2.0 in wVersion since that is the version we
+ * requested.
+ */
+ if (LOBYTE (wsaData.wVersion) != 2 ||
+ HIBYTE (wsaData.wVersion) != 0)
+ {
+ _dbus_assert_not_reached ("No usable WinSock found");
+ _dbus_abort ();
+ }
+
+ beenhere = TRUE;
+
+out:
+ _DBUS_UNLOCK (sysdeps);
+ return TRUE;
+}
+
+
+
+
+
+
+
+
+
+/************************************************************************
+
+ UTF / string code
+
+ ************************************************************************/
+
+/**
+ * Measure the message length without terminating nul
+ */
+int _dbus_printf_string_upper_bound (const char *format,
+ va_list args)
+{
+ /* MSVCRT's vsnprintf semantics are a bit different */
+ char buf[1024];
+ int bufsize;
+ int len;
+ va_list args_copy;
+
+ bufsize = sizeof (buf);
+ va_copy (args_copy, args);
+ len = _vsnprintf (buf, bufsize - 1, format, args_copy);
+ va_end (args_copy);
+
+ while (len == -1) /* try again */
+ {
+ char *p;
+
+ bufsize *= 2;
+
+ p = malloc (bufsize);
+
+ if (p == NULL)
+ return -1;
+
+ va_copy (args_copy, args);
+ len = _vsnprintf (p, bufsize - 1, format, args_copy);
+ va_end (args_copy);
+ free (p);
+ }
+
+ return len;
+}
+
+
+/**
+ * Returns the UTF-16 form of a UTF-8 string. The result should be
+ * freed with dbus_free() when no longer needed.
+ *
+ * @param str the UTF-8 string
+ * @param error return location for error code
+ */
+wchar_t *
+_dbus_win_utf8_to_utf16 (const char *str,
+ DBusError *error)
+{
+ DBusString s;
+ int n;
+ wchar_t *retval;
+
+ _dbus_string_init_const (&s, str);
+
+ if (!_dbus_string_validate_utf8 (&s, 0, _dbus_string_get_length (&s)))
+ {
+ dbus_set_error_const (error, DBUS_ERROR_FAILED, "Invalid UTF-8");
+ return NULL;
+ }
+
+ n = MultiByteToWideChar (CP_UTF8, 0, str, -1, NULL, 0);
+
+ if (n == 0)
+ {
+ _dbus_win_set_error_from_win_error (error, GetLastError ());
+ return NULL;
+ }
+
+ retval = dbus_new (wchar_t, n);
+
+ if (!retval)
+ {
+ _DBUS_SET_OOM (error);
+ return NULL;
+ }
+
+ if (MultiByteToWideChar (CP_UTF8, 0, str, -1, retval, n) != n)
+ {
+ dbus_free (retval);
+ dbus_set_error_const (error, DBUS_ERROR_FAILED, "MultiByteToWideChar inconsistency");
+ return NULL;
+ }
+
+ return retval;
+}
+
+/**
+ * Returns the UTF-8 form of a UTF-16 string. The result should be
+ * freed with dbus_free() when no longer needed.
+ *
+ * @param str the UTF-16 string
+ * @param error return location for error code
+ */
+char *
+_dbus_win_utf16_to_utf8 (const wchar_t *str,
+ DBusError *error)
+{
+ int n;
+ char *retval;
+
+ n = WideCharToMultiByte (CP_UTF8, 0, str, -1, NULL, 0, NULL, NULL);
+
+ if (n == 0)
+ {
+ _dbus_win_set_error_from_win_error (error, GetLastError ());
+ return NULL;
+ }
+
+ retval = dbus_malloc (n);
+
+ if (!retval)
+ {
+ _DBUS_SET_OOM (error);
+ return NULL;
+ }
+
+ if (WideCharToMultiByte (CP_UTF8, 0, str, -1, retval, n, NULL, NULL) != n)
+ {
+ dbus_free (retval);
+ dbus_set_error_const (error, DBUS_ERROR_FAILED, "WideCharToMultiByte inconsistency");
+ return NULL;
+ }
+
+ return retval;
+}
+
+
+
+
+
+
+/************************************************************************
+
+
+ ************************************************************************/
+
+dbus_bool_t
+_dbus_win_account_to_sid (const wchar_t *waccount,
+ void **ppsid,
+ DBusError *error)
+{
+ dbus_bool_t retval = FALSE;
+ DWORD sid_length, wdomain_length;
+ SID_NAME_USE use;
+ wchar_t *wdomain;
+
+ *ppsid = NULL;
+
+ sid_length = 0;
+ wdomain_length = 0;
+ if (!LookupAccountNameW (NULL, waccount, NULL, &sid_length,
+ NULL, &wdomain_length, &use) &&
+ GetLastError () != ERROR_INSUFFICIENT_BUFFER)
+ {
+ _dbus_win_set_error_from_win_error (error, GetLastError ());
+ return FALSE;
+ }
+
+ *ppsid = dbus_malloc (sid_length);
+ if (!*ppsid)
+ {
+ _DBUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ wdomain = dbus_new (wchar_t, wdomain_length);
+ if (!wdomain)
+ {
+ _DBUS_SET_OOM (error);
+ goto out1;
+ }
+
+ if (!LookupAccountNameW (NULL, waccount, (PSID) *ppsid, &sid_length,
+ wdomain, &wdomain_length, &use))
+ {
+ _dbus_win_set_error_from_win_error (error, GetLastError ());
+ goto out2;
+ }
+
+ if (!IsValidSid ((PSID) *ppsid))
+ {
+ dbus_set_error_const (error, DBUS_ERROR_FAILED, "Invalid SID");
+ goto out2;
+ }
+
+ retval = TRUE;
+
+out2:
+ dbus_free (wdomain);
+out1:
+ if (!retval)
+ {
+ dbus_free (*ppsid);
+ *ppsid = NULL;
+ }
+
+ return retval;
+}
+
+/** @} end of sysdeps-win */
+
+
+/**
+ * The only reason this is separate from _dbus_getpid() is to allow it
+ * on Windows for logging but not for other purposes.
+ *
+ * @returns process ID to put in log messages
+ */
+unsigned long
+_dbus_pid_for_log (void)
+{
+ return _dbus_getpid ();
+}
+
+#ifndef DBUS_WINCE
+
+static BOOL
+is_winxp_sp3_or_lower (void)
+{
+ OSVERSIONINFOEX osvi;
+ DWORDLONG dwlConditionMask = 0;
+ int op=VER_LESS_EQUAL;
+
+ // Initialize the OSVERSIONINFOEX structure.
+
+ ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
+ osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
+ osvi.dwMajorVersion = 5;
+ osvi.dwMinorVersion = 1;
+ osvi.wServicePackMajor = 3;
+ osvi.wServicePackMinor = 0;
+
+ // Initialize the condition mask.
+
+ VER_SET_CONDITION (dwlConditionMask, VER_MAJORVERSION, op);
+ VER_SET_CONDITION (dwlConditionMask, VER_MINORVERSION, op);
+ VER_SET_CONDITION (dwlConditionMask, VER_SERVICEPACKMAJOR, op);
+ VER_SET_CONDITION (dwlConditionMask, VER_SERVICEPACKMINOR, op);
+
+ // Perform the test.
+
+ return VerifyVersionInfo(
+ &osvi,
+ VER_MAJORVERSION | VER_MINORVERSION |
+ VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR,
+ dwlConditionMask);
+}
+
+/** Gets our SID
+ * @param sid points to sid buffer, need to be freed with LocalFree()
+ * @param process_id the process id for which the sid should be returned (use 0 for current process)
+ * @returns process sid
+ */
+dbus_bool_t
+_dbus_getsid(char **sid, dbus_pid_t process_id)
+{
+ HANDLE process_token = INVALID_HANDLE_VALUE;
+ TOKEN_USER *token_user = NULL;
+ DWORD n;
+ PSID psid;
+ int retval = FALSE;
+
+ HANDLE process_handle;
+ if (process_id == 0)
+ process_handle = GetCurrentProcess();
+ else if (is_winxp_sp3_or_lower())
+ process_handle = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, process_id);
+ else
+ process_handle = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, process_id);
+
+ if (!OpenProcessToken (process_handle, TOKEN_QUERY, &process_token))
+ {
+ _dbus_win_warn_win_error ("OpenProcessToken failed", GetLastError ());
+ goto failed;
+ }
+ if ((!GetTokenInformation (process_token, TokenUser, NULL, 0, &n)
+ && GetLastError () != ERROR_INSUFFICIENT_BUFFER)
+ || (token_user = alloca (n)) == NULL
+ || !GetTokenInformation (process_token, TokenUser, token_user, n, &n))
+ {
+ _dbus_win_warn_win_error ("GetTokenInformation failed", GetLastError ());
+ goto failed;
+ }
+ psid = token_user->User.Sid;
+ if (!IsValidSid (psid))
+ {
+ _dbus_verbose("invalid sid\n");
+ goto failed;
+ }
+ if (!ConvertSidToStringSidA (psid, sid))
+ {
+ _dbus_verbose("invalid sid\n");
+ goto failed;
+ }
+//okay:
+ retval = TRUE;
+
+failed:
+ CloseHandle (process_handle);
+ if (process_token != INVALID_HANDLE_VALUE)
+ CloseHandle (process_token);
+
+ _dbus_verbose("_dbus_getsid() got '%s' and returns %d\n", *sid, retval);
+ return retval;
+}
+#endif
+
+/************************************************************************
+
+ pipes
+
+ ************************************************************************/
+
+/**
+ * Creates pair of connect sockets (as in socketpair()).
+ * Sets both ends of the pair nonblocking.
+ *
+ * Marks both file descriptors as close-on-exec
+ *
+ * @param fd1 return location for one end
+ * @param fd2 return location for the other end
+ * @param blocking #TRUE if pair should be blocking
+ * @param error error return
+ * @returns #FALSE on failure (if error is set)
+ */
+dbus_bool_t
+_dbus_socketpair (DBusSocket *fd1,
+ DBusSocket *fd2,
+ dbus_bool_t blocking,
+ DBusError *error)
+{
+ SOCKET temp, socket1 = -1, socket2 = -1;
+ struct sockaddr_in saddr;
+ int len;
+ u_long arg;
+
+ if (!_dbus_win_startup_winsock ())
+ {
+ _DBUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ temp = socket (AF_INET, SOCK_STREAM, 0);
+ if (temp == INVALID_SOCKET)
+ {
+ DBUS_SOCKET_SET_ERRNO ();
+ goto out0;
+ }
+
+ _DBUS_ZERO (saddr);
+ saddr.sin_family = AF_INET;
+ saddr.sin_port = 0;
+ saddr.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
+
+ if (bind (temp, (struct sockaddr *)&saddr, sizeof (saddr)) == SOCKET_ERROR)
+ {
+ DBUS_SOCKET_SET_ERRNO ();
+ goto out0;
+ }
+
+ if (listen (temp, 1) == SOCKET_ERROR)
+ {
+ DBUS_SOCKET_SET_ERRNO ();
+ goto out0;
+ }
+
+ len = sizeof (saddr);
+ if (getsockname (temp, (struct sockaddr *)&saddr, &len) == SOCKET_ERROR)
+ {
+ DBUS_SOCKET_SET_ERRNO ();
+ goto out0;
+ }
+
+ socket1 = socket (AF_INET, SOCK_STREAM, 0);
+ if (socket1 == INVALID_SOCKET)
+ {
+ DBUS_SOCKET_SET_ERRNO ();
+ goto out0;
+ }
+
+ if (connect (socket1, (struct sockaddr *)&saddr, len) == SOCKET_ERROR)
+ {
+ DBUS_SOCKET_SET_ERRNO ();
+ goto out1;
+ }
+
+ socket2 = accept (temp, (struct sockaddr *) &saddr, &len);
+ if (socket2 == INVALID_SOCKET)
+ {
+ DBUS_SOCKET_SET_ERRNO ();
+ goto out1;
+ }
+
+ if (!blocking)
+ {
+ arg = 1;
+ if (ioctlsocket (socket1, FIONBIO, &arg) == SOCKET_ERROR)
+ {
+ DBUS_SOCKET_SET_ERRNO ();
+ goto out2;
+ }
+
+ arg = 1;
+ if (ioctlsocket (socket2, FIONBIO, &arg) == SOCKET_ERROR)
+ {
+ DBUS_SOCKET_SET_ERRNO ();
+ goto out2;
+ }
+ }
+
+ fd1->sock = socket1;
+ fd2->sock = socket2;
+
+ _dbus_verbose ("full-duplex pipe %Iu:%Iu <-> %Iu:%Iu\n",
+ fd1->sock, socket1, fd2->sock, socket2);
+
+ closesocket (temp);
+
+ return TRUE;
+
+out2:
+ closesocket (socket2);
+out1:
+ closesocket (socket1);
+out0:
+ closesocket (temp);
+
+ dbus_set_error (error, _dbus_error_from_errno (errno),
+ "Could not setup socket pair: %s",
+ _dbus_strerror_from_errno ());
+
+ return FALSE;
+}
+
+#ifdef DBUS_ENABLE_VERBOSE_MODE
+static dbus_bool_t
+_dbus_dump_fd_events (DBusPollFD *fds, int n_fds)
+{
+ DBusString msg = _DBUS_STRING_INIT_INVALID;
+ dbus_bool_t result = FALSE;
+ int i;
+
+ if (!_dbus_string_init (&msg))
+ goto oom;
+
+ for (i = 0; i < n_fds; i++)
+ {
+ DBusPollFD *fdp = &fds[i];
+ if (!_dbus_string_append (&msg, i > 0 ? "\n\t" : "\t"))
+ goto oom;
+
+ if ((fdp->events & _DBUS_POLLIN) &&
+ !_dbus_string_append_printf (&msg, "R:%Iu ", fdp->fd.sock))
+ goto oom;
+
+ if ((fdp->events & _DBUS_POLLOUT) &&
+ !_dbus_string_append_printf (&msg, "W:%Iu ", fdp->fd.sock))
+ goto oom;
+
+ if (!_dbus_string_append_printf (&msg, "E:%Iu", fdp->fd.sock))
+ goto oom;
+ }
+
+ _dbus_verbose ("%s\n", _dbus_string_get_const_data (&msg));
+ result = TRUE;
+oom:
+ _dbus_string_free (&msg);
+ return result;
+}
+
+#ifdef USE_CHRIS_IMPL
+static dbus_bool_t
+_dbus_dump_fd_revents (DBusPollFD *fds, int n_fds)
+{
+ DBusString msg = _DBUS_STRING_INIT_INVALID;
+ dbus_bool_t result = FALSE;
+ int i;
+
+ if (!_dbus_string_init (&msg))
+ goto oom;
+
+ for (i = 0; i < n_fds; i++)
+ {
+ DBusPollFD *fdp = &fds[i];
+ if (!_dbus_string_append (&msg, i > 0 ? "\n\t" : "\t"))
+ goto oom;
+
+ if ((fdp->revents & _DBUS_POLLIN) &&
+ !_dbus_string_append_printf (&msg, "R:%Iu ", fdp->fd.sock))
+ goto oom;
+
+ if ((fdp->revents & _DBUS_POLLOUT) &&
+ !_dbus_string_append_printf (&msg, "W:%Iu ", fdp->fd.sock))
+ goto oom;
+
+ if ((fdp->revents & _DBUS_POLLERR) &&
+ !_dbus_string_append_printf (&msg, "E:%Iu", fdp->fd.sock))
+ goto oom;
+ }
+
+ _dbus_verbose ("%s\n", _dbus_string_get_const_data (&msg));
+ result = TRUE;
+oom:
+ _dbus_string_free (&msg);
+ return result;
+}
+#else
+static dbus_bool_t
+_dbus_dump_fdset (DBusPollFD *fds, int n_fds, fd_set *read_set, fd_set *write_set, fd_set *err_set)
+{
+ DBusString msg = _DBUS_STRING_INIT_INVALID;
+ dbus_bool_t result = FALSE;
+ int i;
+
+ if (!_dbus_string_init (&msg))
+ goto oom;
+
+ for (i = 0; i < n_fds; i++)
+ {
+ DBusPollFD *fdp = &fds[i];
+
+ if (!_dbus_string_append (&msg, i > 0 ? "\n\t" : "\t"))
+ goto oom;
+
+ if (FD_ISSET (fdp->fd.sock, read_set) &&
+ !_dbus_string_append_printf (&msg, "R:%Iu ", fdp->fd.sock))
+ goto oom;
+
+ if (FD_ISSET (fdp->fd.sock, write_set) &&
+ !_dbus_string_append_printf (&msg, "W:%Iu ", fdp->fd.sock))
+ goto oom;
+
+ if (FD_ISSET (fdp->fd.sock, err_set) &&
+ !_dbus_string_append_printf (&msg, "E:%Iu", fdp->fd.sock))
+ goto oom;
+ }
+ _dbus_verbose ("%s\n", _dbus_string_get_const_data (&msg));
+ result = TRUE;
+oom:
+ _dbus_string_free (&msg);
+ return result;
+}
+#endif
+#endif
+
+#ifdef USE_CHRIS_IMPL
+/**
+ * Windows event based implementation for _dbus_poll().
+ *
+ * @param fds the file descriptors to poll
+ * @param n_fds number of descriptors in the array
+ * @param timeout_milliseconds timeout or -1 for infinite
+ * @returns numbers of fds with revents, or <0 on error
+ */
+static int
+_dbus_poll_events (DBusPollFD *fds,
+ int n_fds,
+ int timeout_milliseconds)
+{
+ int ret = 0;
+ int i;
+ DWORD ready;
+
+#define DBUS_STACK_WSAEVENTS 256
+ WSAEVENT eventsOnStack[DBUS_STACK_WSAEVENTS];
+ WSAEVENT *pEvents = NULL;
+ if (n_fds > DBUS_STACK_WSAEVENTS)
+ pEvents = calloc(sizeof(WSAEVENT), n_fds);
+ else
+ pEvents = eventsOnStack;
+
+ if (pEvents == NULL)
+ {
+ _dbus_win_set_errno (ENOMEM);
+ ret = -1;
+ goto oom;
+ }
+
+#ifdef DBUS_ENABLE_VERBOSE_MODE
+ _dbus_verbose ("_dbus_poll: to=%d", timeout_milliseconds);
+ if (!_dbus_dump_fd_events (fds, n_fds))
+ {
+ _dbus_win_set_errno (ENOMEM);
+ ret = -1;
+ goto oom;
+ }
+#endif
+
+ for (i = 0; i < n_fds; i++)
+ pEvents[i] = WSA_INVALID_EVENT;
+
+ for (i = 0; i < n_fds; i++)
+ {
+ DBusPollFD *fdp = &fds[i];
+ WSAEVENT ev;
+ long lNetworkEvents = FD_OOB;
+
+ ev = WSACreateEvent();
+
+ if (fdp->events & _DBUS_POLLIN)
+ lNetworkEvents |= FD_READ | FD_ACCEPT | FD_CLOSE;
+
+ if (fdp->events & _DBUS_POLLOUT)
+ lNetworkEvents |= FD_WRITE | FD_CONNECT;
+
+ WSAEventSelect (fdp->fd.sock, ev, lNetworkEvents);
+
+ pEvents[i] = ev;
+ }
+
+ ready = WSAWaitForMultipleEvents (n_fds, pEvents, FALSE, timeout_milliseconds, FALSE);
+
+ if (ready == WSA_WAIT_FAILED)
+ {
+ DBUS_SOCKET_SET_ERRNO ();
+ if (errno != WSAEWOULDBLOCK)
+ _dbus_verbose ("WSAWaitForMultipleEvents: failed: %s\n", _dbus_strerror_from_errno ());
+ ret = -1;
+ }
+ else if (ready == WSA_WAIT_TIMEOUT)
+ {
+ _dbus_verbose ("WSAWaitForMultipleEvents: WSA_WAIT_TIMEOUT\n");
+ ret = 0;
+ }
+ else if (ready < (WSA_WAIT_EVENT_0 + n_fds))
+ {
+ for (i = 0; i < n_fds; i++)
+ {
+ DBusPollFD *fdp = &fds[i];
+ WSANETWORKEVENTS ne;
+
+ fdp->revents = 0;
+
+ WSAEnumNetworkEvents (fdp->fd.sock, pEvents[i], &ne);
+
+ if (ne.lNetworkEvents & (FD_READ | FD_ACCEPT | FD_CLOSE))
+ fdp->revents |= _DBUS_POLLIN;
+
+ if (ne.lNetworkEvents & (FD_WRITE | FD_CONNECT))
+ fdp->revents |= _DBUS_POLLOUT;
+
+ if (ne.lNetworkEvents & (FD_OOB))
+ fdp->revents |= _DBUS_POLLERR;
+
+ if(ne.lNetworkEvents)
+ ret++;
+
+ WSAEventSelect (fdp->fd.sock, pEvents[i], 0);
+ }
+#ifdef DBUS_ENABLE_VERBOSE_MODE
+ _dbus_verbose ("_dbus_poll: to=%d", timeout_milliseconds);
+ if (!_dbus_dump_fd_revents (fds, n_fds))
+ {
+ _dbus_win_set_errno (ENOMEM);
+ ret = -1;
+ goto oom;
+ }
+#endif
+ }
+ else
+ {
+ _dbus_verbose ("WSAWaitForMultipleEvents: failed for unknown reason!");
+ ret = -1;
+ }
+
+oom:
+ if (pEvents != NULL)
+ {
+ for (i = 0; i < n_fds; i++)
+ {
+ if (pEvents[i] != WSA_INVALID_EVENT)
+ WSACloseEvent (pEvents[i]);
+ }
+ if (n_fds > DBUS_STACK_WSAEVENTS)
+ free (pEvents);
+ }
+
+ return ret;
+}
+#else
+/**
+ * Select based implementation for _dbus_poll().
+ *
+ * @param fds the file descriptors to poll
+ * @param n_fds number of descriptors in the array
+ * @param timeout_milliseconds timeout or -1 for infinite
+ * @returns numbers of fds with revents, or <0 on error
+ */
+static int
+_dbus_poll_select (DBusPollFD *fds,
+ int n_fds,
+ int timeout_milliseconds)
+{
+ fd_set read_set, write_set, err_set;
+ SOCKET max_fd = 0;
+ int i;
+ struct timeval tv;
+ int ready;
+
+ FD_ZERO (&read_set);
+ FD_ZERO (&write_set);
+ FD_ZERO (&err_set);
+#ifdef DBUS_ENABLE_VERBOSE_MODE
+ _dbus_verbose("_dbus_poll: to=%d\n", timeout_milliseconds);
+ if (!_dbus_dump_fd_events (fds, n_fds))
+ return -1;
+#endif
+
+ for (i = 0; i < n_fds; i++)
+ {
+ DBusPollFD *fdp = &fds[i];
+
+ if (fdp->events & _DBUS_POLLIN)
+ FD_SET (fdp->fd.sock, &read_set);
+
+ if (fdp->events & _DBUS_POLLOUT)
+ FD_SET (fdp->fd.sock, &write_set);
+
+ FD_SET (fdp->fd.sock, &err_set);
+
+ max_fd = MAX (max_fd, fdp->fd.sock);
+ }
+
+ // Avoid random lockups with send(), for lack of a better solution so far
+ tv.tv_sec = timeout_milliseconds < 0 ? 1 : timeout_milliseconds / 1000;
+ tv.tv_usec = timeout_milliseconds < 0 ? 0 : (timeout_milliseconds % 1000) * 1000;
+
+ ready = select (max_fd + 1, &read_set, &write_set, &err_set, &tv);
+
+ if (DBUS_SOCKET_API_RETURNS_ERROR (ready))
+ {
+ DBUS_SOCKET_SET_ERRNO ();
+ if (errno != WSAEWOULDBLOCK)
+ _dbus_verbose ("select: failed: %s\n", _dbus_strerror_from_errno ());
+ }
+ else if (ready == 0)
+ _dbus_verbose ("select: = 0\n");
+ else
+ if (ready > 0)
+ {
+#ifdef DBUS_ENABLE_VERBOSE_MODE
+ _dbus_verbose ("select: to=%d\n", ready);
+ if (!_dbus_dump_fdset (fds, n_fds, &read_set, &write_set, &err_set))
+ {
+ _dbus_win_set_errno (ENOMEM);
+ return -1;
+ }
+#endif
+ for (i = 0; i < n_fds; i++)
+ {
+ DBusPollFD *fdp = &fds[i];
+
+ fdp->revents = 0;
+
+ if (FD_ISSET (fdp->fd.sock, &read_set))
+ fdp->revents |= _DBUS_POLLIN;
+
+ if (FD_ISSET (fdp->fd.sock, &write_set))
+ fdp->revents |= _DBUS_POLLOUT;
+
+ if (FD_ISSET (fdp->fd.sock, &err_set))
+ fdp->revents |= _DBUS_POLLERR;
+ }
+ }
+ return ready;
+}
+#endif
+
+/**
+ * Wrapper for poll().
+ *
+ * @param fds the file descriptors to poll
+ * @param n_fds number of descriptors in the array
+ * @param timeout_milliseconds timeout or -1 for infinite
+ * @returns numbers of fds with revents, or <0 on error
+ */
+int
+_dbus_poll (DBusPollFD *fds,
+ int n_fds,
+ int timeout_milliseconds)
+{
+#ifdef USE_CHRIS_IMPL
+ return _dbus_poll_events (fds, n_fds, timeout_milliseconds);
+#else
+ return _dbus_poll_select (fds, n_fds, timeout_milliseconds);
+#endif
+}
+
+/******************************************************************************
+
+Original CVS version of dbus-sysdeps.c
+
+******************************************************************************/
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-sysdeps.c Wrappers around system/libc features (internal to D-Bus implementation)
+ *
+ * Copyright (C) 2002, 2003 Red Hat, Inc.
+ * Copyright (C) 2003 CodeFactory AB
+ * Copyright (C) 2005 Novell, Inc.
+ *
+ * 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
+ *
+ */
+
+
+/**
+ * Exit the process, returning the given value.
+ *
+ * @param code the exit code
+ */
+void
+_dbus_exit (int code)
+{
+ _exit (code);
+}
+
+/**
+ * Creates a socket and connects to a socket at the given host
+ * and port. The connection fd is returned, and is set up as
+ * nonblocking.
+ *
+ * @param host the host name to connect to
+ * @param port the port to connect to
+ * @param family the address family to listen on, NULL for all
+ * @param error return location for error code
+ * @returns connection file descriptor or -1 on error
+ */
+DBusSocket
+_dbus_connect_tcp_socket (const char *host,
+ const char *port,
+ const char *family,
+ DBusError *error)
+{
+ return _dbus_connect_tcp_socket_with_nonce (host, port, family, (const char*)NULL, error);
+}
+
+DBusSocket
+_dbus_connect_tcp_socket_with_nonce (const char *host,
+ const char *port,
+ const char *family,
+ const char *noncefile,
+ DBusError *error)
+{
+ int saved_errno = 0;
+ DBusList *connect_errors = NULL;
+ DBusSocket fd = DBUS_SOCKET_INIT;
+ int res;
+ struct addrinfo hints;
+ struct addrinfo *ai = NULL;
+ const struct addrinfo *tmp;
+ DBusError *connect_error;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ if (!_dbus_win_startup_winsock ())
+ {
+ _DBUS_SET_OOM (error);
+ return _dbus_socket_get_invalid ();
+ }
+
+ _DBUS_ZERO (hints);
+
+ if (!family)
+ hints.ai_family = AF_UNSPEC;
+ else if (!strcmp(family, "ipv4"))
+ hints.ai_family = AF_INET;
+ else if (!strcmp(family, "ipv6"))
+ hints.ai_family = AF_INET6;
+ else
+ {
+ dbus_set_error (error,
+ DBUS_ERROR_BAD_ADDRESS,
+ "Unknown address family %s", family);
+ return _dbus_socket_get_invalid ();
+ }
+ hints.ai_protocol = IPPROTO_TCP;
+ hints.ai_socktype = SOCK_STREAM;
+#ifdef AI_ADDRCONFIG
+ hints.ai_flags = AI_ADDRCONFIG;
+#else
+ hints.ai_flags = 0;
+#endif
+
+ if ((res = getaddrinfo(host, port, &hints, &ai)) != 0 || !ai)
+ {
+ dbus_set_error (error,
+ _dbus_error_from_errno (res),
+ "Failed to lookup host/port: \"%s:%s\": %s (%d)",
+ host, port, _dbus_strerror (res), res);
+ goto out;
+ }
+
+ tmp = ai;
+ while (tmp)
+ {
+ if ((fd.sock = socket (tmp->ai_family, SOCK_STREAM, 0)) == INVALID_SOCKET)
+ {
+ saved_errno = _dbus_get_low_level_socket_errno ();
+ dbus_set_error (error,
+ _dbus_error_from_errno (saved_errno),
+ "Failed to open socket: %s",
+ _dbus_strerror (saved_errno));
+ _dbus_assert (!_dbus_socket_is_valid (fd));
+ goto out;
+ }
+ _DBUS_ASSERT_ERROR_IS_CLEAR(error);
+
+ if (connect (fd.sock, (struct sockaddr*) tmp->ai_addr, tmp->ai_addrlen) == SOCKET_ERROR)
+ {
+ saved_errno = _dbus_get_low_level_socket_errno ();
+ _dbus_close_socket (&fd, NULL);
+
+ connect_error = dbus_new0 (DBusError, 1);
+
+ if (connect_error == NULL)
+ {
+ _DBUS_SET_OOM (error);
+ goto out;
+ }
+
+ dbus_error_init (connect_error);
+ _dbus_set_error_with_inet_sockaddr (connect_error,
+ tmp->ai_addr, tmp->ai_addrlen,
+ "Failed to connect to socket",
+ saved_errno);
+
+ if (!_dbus_list_append (&connect_errors, connect_error))
+ {
+ dbus_error_free (connect_error);
+ dbus_free (connect_error);
+ _DBUS_SET_OOM (error);
+ goto out;
+ }
+
+ tmp = tmp->ai_next;
+ continue;
+ }
+
+ break;
+ }
+
+ if (!_dbus_socket_is_valid (fd))
+ {
+ _dbus_combine_tcp_errors (&connect_errors, "Failed to connect",
+ host, port, error);
+ goto out;
+ }
+
+ if (noncefile != NULL)
+ {
+ DBusString noncefileStr;
+ dbus_bool_t ret;
+ _dbus_string_init_const (&noncefileStr, noncefile);
+ ret = _dbus_send_nonce (fd, &noncefileStr, error);
+
+ if (!ret)
+ {
+ _dbus_close_socket (&fd, NULL);
+ goto out;
+ }
+ }
+
+ /* Every SOCKET is also a HANDLE. */
+ _dbus_win_handle_set_close_on_exec ((HANDLE) fd.sock);
+
+ if (!_dbus_set_socket_nonblocking (fd, error))
+ {
+ _dbus_close_socket (&fd, NULL);
+ goto out;
+ }
+
+out:
+ if (ai != NULL)
+ freeaddrinfo (ai);
+
+ while ((connect_error = _dbus_list_pop_first (&connect_errors)))
+ {
+ dbus_error_free (connect_error);
+ dbus_free (connect_error);
+ }
+
+ return fd;
+}
+
+/**
+ * Creates a socket and binds it to the given path, then listens on
+ * the socket. The socket is set to be nonblocking. In case of port=0
+ * a random free port is used and returned in the port parameter.
+ * If inaddr_any is specified, the hostname is ignored.
+ *
+ * @param host the host name to listen on
+ * @param port the port to listen on, if zero a free port will be used
+ * @param family the address family to listen on, NULL for all
+ * @param retport string to return the actual port listened on
+ * @param retfamily string to return the actual family listened on
+ * @param fds_p location to store returned file descriptors
+ * @param error return location for errors
+ * @returns the number of listening file descriptors or -1 on error
+ */
+int
+_dbus_listen_tcp_socket (const char *host,
+ const char *port,
+ const char *family,
+ DBusString *retport,
+ const char **retfamily,
+ DBusSocket **fds_p,
+ DBusError *error)
+{
+ int saved_errno;
+ int nlisten_fd = 0, res, i, port_num = -1;
+ DBusList *bind_errors = NULL;
+ DBusError *bind_error = NULL;
+ DBusSocket *listen_fd = NULL;
+ struct addrinfo hints;
+ struct addrinfo *ai, *tmp;
+ dbus_bool_t have_ipv4 = FALSE;
+ dbus_bool_t have_ipv6 = FALSE;
+
+ // On Vista, sockaddr_gen must be a sockaddr_in6, and not a sockaddr_in6_old
+ //That's required for family == IPv6(which is the default on Vista if family is not given)
+ //So we use our own union instead of sockaddr_gen:
+
+ typedef union {
+ struct sockaddr Address;
+ struct sockaddr_in AddressIn;
+ struct sockaddr_in6 AddressIn6;
+ } mysockaddr_gen;
+
+ *fds_p = NULL;
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ if (!_dbus_win_startup_winsock ())
+ {
+ _DBUS_SET_OOM (error);
+ return -1;
+ }
+
+ _DBUS_ZERO (hints);
+
+ if (!family)
+ hints.ai_family = AF_UNSPEC;
+ else if (!strcmp(family, "ipv4"))
+ hints.ai_family = AF_INET;
+ else if (!strcmp(family, "ipv6"))
+ hints.ai_family = AF_INET6;
+ else
+ {
+ dbus_set_error (error,
+ DBUS_ERROR_INVALID_ARGS,
+ "Unknown address family %s", family);
+ return -1;
+ }
+
+ hints.ai_protocol = IPPROTO_TCP;
+ hints.ai_socktype = SOCK_STREAM;
+#ifdef AI_ADDRCONFIG
+ hints.ai_flags = AI_ADDRCONFIG | AI_PASSIVE;
+#else
+ hints.ai_flags = AI_PASSIVE;
+#endif
+
+ redo_lookup_with_port:
+ ai = NULL;
+ if ((res = getaddrinfo(host, port, &hints, &ai)) != 0 || !ai)
+ {
+ dbus_set_error (error,
+ _dbus_error_from_errno (res),
+ "Failed to lookup host/port: \"%s:%s\": %s (%d)",
+ host ? host : "*", port, _dbus_strerror(res), res);
+ return -1;
+ }
+
+ tmp = ai;
+ while (tmp)
+ {
+ const int reuseaddr = 1, tcp_nodelay_on = 1;
+ DBusSocket fd = DBUS_SOCKET_INIT, *newlisten_fd;
+
+ if ((fd.sock = socket (tmp->ai_family, SOCK_STREAM, 0)) == INVALID_SOCKET)
+ {
+ saved_errno = _dbus_get_low_level_socket_errno ();
+ dbus_set_error (error,
+ _dbus_error_from_errno (saved_errno),
+ "Failed to open socket: %s",
+ _dbus_strerror (saved_errno));
+ _dbus_assert (!_dbus_socket_is_valid (fd));
+ goto failed;
+ }
+ _DBUS_ASSERT_ERROR_IS_CLEAR(error);
+
+ if (setsockopt (fd.sock, SOL_SOCKET, SO_REUSEADDR, (const char *)&reuseaddr, sizeof(reuseaddr)) == SOCKET_ERROR)
+ {
+ saved_errno = _dbus_get_low_level_socket_errno ();
+ _dbus_warn ("Failed to set socket option \"%s:%s\": %s",
+ host ? host : "*", port, _dbus_strerror (saved_errno));
+ }
+
+ /* Nagle's algorithm imposes a huge delay on the initial messages
+ going over TCP. */
+ if (setsockopt (fd.sock, IPPROTO_TCP, TCP_NODELAY, (const char *)&tcp_nodelay_on, sizeof (tcp_nodelay_on)) == SOCKET_ERROR)
+ {
+ saved_errno = _dbus_get_low_level_socket_errno ();
+ _dbus_warn ("Failed to set TCP_NODELAY socket option \"%s:%s\": %s",
+ host ? host : "*", port, _dbus_strerror (saved_errno));
+ }
+
+ if (bind (fd.sock, (struct sockaddr *) tmp->ai_addr, tmp->ai_addrlen) == SOCKET_ERROR)
+ {
+ saved_errno = _dbus_get_low_level_socket_errno ();
+ closesocket (fd.sock);
+
+ /*
+ * We don't treat this as a fatal error, because there might be
+ * other addresses that we can listen on. In particular:
+ *
+ * - If saved_errno is WSAEADDRINUSE after we
+ * "goto redo_lookup_with_port" after binding a port on one of the
+ * possible addresses, we will try to bind that same port on
+ * every address, including the same address again for a second
+ * time, which will fail with WSAEADDRINUSE .
+ *
+ * - If saved_errno is WSAEADDRINUSE, it might be because binding to
+ * an IPv6 address implicitly binds to a corresponding IPv4
+ * address or vice versa.
+ *
+ * - If saved_errno is WSAEADDRNOTAVAIL when we asked for family
+ * AF_UNSPEC, it might be because IPv6 is disabled for this
+ * particular interface.
+ */
+ bind_error = dbus_new0 (DBusError, 1);
+
+ if (bind_error == NULL)
+ {
+ _DBUS_SET_OOM (error);
+ goto failed;
+ }
+
+ dbus_error_init (bind_error);
+ _dbus_set_error_with_inet_sockaddr (bind_error, tmp->ai_addr, tmp->ai_addrlen,
+ "Failed to bind socket",
+ saved_errno);
+
+ if (!_dbus_list_append (&bind_errors, bind_error))
+ {
+ dbus_error_free (bind_error);
+ dbus_free (bind_error);
+ _DBUS_SET_OOM (error);
+ goto failed;
+ }
+
+ /* Try the next address, maybe it will work better */
+ tmp = tmp->ai_next;
+ continue;
+ }
+
+ if (listen (fd.sock, 30 /* backlog */) == SOCKET_ERROR)
+ {
+ saved_errno = _dbus_get_low_level_socket_errno ();
+ closesocket (fd.sock);
+ _dbus_set_error_with_inet_sockaddr (error, tmp->ai_addr, tmp->ai_addrlen,
+ "Failed to listen on socket",
+ saved_errno);
+ goto failed;
+ }
+
+ newlisten_fd = dbus_realloc(listen_fd, sizeof(DBusSocket)*(nlisten_fd+1));
+ if (!newlisten_fd)
+ {
+ closesocket (fd.sock);
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY,
+ "Failed to allocate file handle array");
+ goto failed;
+ }
+ listen_fd = newlisten_fd;
+ listen_fd[nlisten_fd] = fd;
+ nlisten_fd++;
+
+ if (tmp->ai_addr->sa_family == AF_INET)
+ have_ipv4 = TRUE;
+ else if (tmp->ai_addr->sa_family == AF_INET6)
+ have_ipv6 = TRUE;
+
+ if (!_dbus_string_get_length(retport))
+ {
+ /* If the user didn't specify a port, or used 0, then
+ the kernel chooses a port. After the first address
+ is bound to, we need to force all remaining addresses
+ to use the same port */
+ if (!port || !strcmp(port, "0"))
+ {
+ mysockaddr_gen addr;
+ socklen_t addrlen = sizeof(addr);
+ char portbuf[NI_MAXSERV];
+
+ if (getsockname (fd.sock, &addr.Address, &addrlen) == SOCKET_ERROR ||
+ (res = getnameinfo (&addr.Address, addrlen, NULL, 0,
+ portbuf, sizeof(portbuf),
+ NI_NUMERICSERV)) != 0)
+ {
+ saved_errno = _dbus_get_low_level_socket_errno ();
+ dbus_set_error (error, _dbus_error_from_errno (saved_errno),
+ "Failed to resolve port \"%s:%s\": %s",
+ host ? host : "*", port, _dbus_strerror (saved_errno));
+ goto failed;
+ }
+ if (!_dbus_string_append(retport, portbuf))
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ goto failed;
+ }
+
+ /* Release current address list & redo lookup */
+ port = _dbus_string_get_const_data(retport);
+ freeaddrinfo(ai);
+ goto redo_lookup_with_port;
+ }
+ else
+ {
+ if (!_dbus_string_append(retport, port))
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ goto failed;
+ }
+ }
+ }
+
+ tmp = tmp->ai_next;
+ }
+ freeaddrinfo(ai);
+ ai = NULL;
+
+ if (!nlisten_fd)
+ {
+ _dbus_combine_tcp_errors (&bind_errors, "Failed to bind", host, port, error);
+ goto failed;
+ }
+
+ if (have_ipv4 && !have_ipv6)
+ *retfamily = "ipv4";
+ else if (!have_ipv4 && have_ipv6)
+ *retfamily = "ipv6";
+
+ sscanf(_dbus_string_get_const_data(retport), "%d", &port_num);
+
+ for (i = 0 ; i < nlisten_fd ; i++)
+ {
+ _dbus_win_handle_set_close_on_exec ((HANDLE) listen_fd[i].sock);
+ if (!_dbus_set_socket_nonblocking (listen_fd[i], error))
+ {
+ goto failed;
+ }
+ }
+
+ *fds_p = listen_fd;
+
+ /* This list might be non-empty even on success, because we might be
+ * ignoring WSAEADDRINUSE or WSAEADDRNOTAVAIL */
+ while ((bind_error = _dbus_list_pop_first (&bind_errors)))
+ {
+ dbus_error_free (bind_error);
+ dbus_free (bind_error);
+ }
+ return nlisten_fd;
+
+ failed:
+ if (ai)
+ freeaddrinfo(ai);
+ for (i = 0 ; i < nlisten_fd ; i++)
+ closesocket (listen_fd[i].sock);
+
+ while ((bind_error = _dbus_list_pop_first (&bind_errors)))
+ {
+ dbus_error_free (bind_error);
+ dbus_free (bind_error);
+ }
+
+ dbus_free(listen_fd);
+ return -1;
+}
+
+
+/**
+ * Accepts a connection on a listening socket.
+ * Handles EINTR for you.
+ *
+ * @param listen_fd the listen file descriptor
+ * @returns the connection fd of the client, or -1 on error
+ */
+DBusSocket
+_dbus_accept (DBusSocket listen_fd)
+{
+ DBusSocket client_fd;
+
+ retry:
+ client_fd.sock = accept (listen_fd.sock, NULL, NULL);
+
+ if (!_dbus_socket_is_valid (client_fd))
+ {
+ DBUS_SOCKET_SET_ERRNO ();
+ if (errno == EINTR)
+ goto retry;
+ }
+
+ _dbus_verbose ("client fd %Iu accepted\n", client_fd.sock);
+
+ return client_fd;
+}
+
+
+
+
+dbus_bool_t
+_dbus_send_credentials_socket (DBusSocket handle,
+ DBusError *error)
+{
+/* FIXME: for the session bus credentials shouldn't matter (?), but
+ * for the system bus they are presumably essential. A rough outline
+ * of a way to implement the credential transfer would be this:
+ *
+ * client waits to *read* a byte.
+ *
+ * server creates a named pipe with a random name, sends a byte
+ * contining its length, and its name.
+ *
+ * client reads the name, connects to it (using Win32 API).
+ *
+ * server waits for connection to the named pipe, then calls
+ * ImpersonateNamedPipeClient(), notes its now-current credentials,
+ * calls RevertToSelf(), closes its handles to the named pipe, and
+ * is done. (Maybe there is some other way to get the SID of a named
+ * pipe client without having to use impersonation?)
+ *
+ * client closes its handles and is done.
+ *
+ * Ralf: Why not sending credentials over the given this connection ?
+ * Using named pipes makes it impossible to be connected from a unix client.
+ *
+ */
+ int bytes_written;
+ DBusString buf;
+
+ _dbus_string_init_const_len (&buf, "\0", 1);
+again:
+ bytes_written = _dbus_write_socket (handle, &buf, 0, 1 );
+
+ if (bytes_written < 0 && errno == EINTR)
+ goto again;
+
+ if (bytes_written < 0)
+ {
+ dbus_set_error (error, _dbus_error_from_errno (errno),
+ "Failed to write credentials byte: %s",
+ _dbus_strerror_from_errno ());
+ return FALSE;
+ }
+ else if (bytes_written == 0)
+ {
+ dbus_set_error (error, DBUS_ERROR_IO_ERROR,
+ "wrote zero bytes writing credentials byte");
+ return FALSE;
+ }
+ else
+ {
+ _dbus_assert (bytes_written == 1);
+ _dbus_verbose ("wrote 1 zero byte, credential sending isn't implemented yet\n");
+ return TRUE;
+ }
+ return TRUE;
+}
+
+#ifdef HAVE_AFUNIX_H
+/*
+ * Returns false with no error set if the socket is non-AF_UNIX
+ * (contrary to our usual convention).
+ *
+ * Returns false with an error set on failure to identify it.
+ */
+static dbus_bool_t
+_dbus_socket_is_af_unix (DBusSocket s,
+ DBusError *error)
+{
+ struct sockaddr_un saddr;
+ int len;
+
+ len = sizeof (saddr);
+ if (getsockname (s.sock, (struct sockaddr *)&saddr, &len) == SOCKET_ERROR)
+ {
+ DBUS_SOCKET_SET_ERRNO ();
+ dbus_set_error (error, _dbus_error_from_errno (errno),
+ "Failed to getsockname: %s",
+ _dbus_strerror_from_errno ());
+ return FALSE;
+ }
+
+ return saddr.sun_family == AF_UNIX;
+}
+
+/**
+ * @brief return peer process id from Unix domain socket handle
+ * @param handle AF_UNIX socket descriptor
+ * @return process id or 0 in case the process id could not be fetched
+ */
+static dbus_pid_t
+_dbus_get_peer_pid_from_uds_handle (int handle)
+{
+ DWORD pid, drc;
+
+ if (WSAIoctl (handle, SIO_AF_UNIX_GETPEERPID,
+ NULL, 0U,
+ &pid, sizeof (pid), &drc,
+ NULL, NULL) == SOCKET_ERROR)
+ {
+ _dbus_verbose ("failed to get peer's pid\n");
+ return 0;
+ }
+
+ return pid;
+}
+#endif
+
+/**
+ * Reads a single byte which must be nul (an error occurs otherwise),
+ * and reads unix credentials if available. Fills in pid/uid/gid with
+ * -1 if no credentials are available. Return value indicates whether
+ * a byte was read, not whether we got valid credentials. On some
+ * systems, such as Linux, reading/writing the byte isn't actually
+ * required, but we do it anyway just to avoid multiple codepaths.
+ *
+ * Fails if no byte is available, so you must select() first.
+ *
+ * The point of the byte is that on some systems we have to
+ * use sendmsg()/recvmsg() to transmit credentials.
+ *
+ * @param handle the client file descriptor
+ * @param credentials struct to fill with credentials of client
+ * @param error location to store error code
+ * @returns #TRUE on success
+ */
+dbus_bool_t
+_dbus_read_credentials_socket (DBusSocket handle,
+ DBusCredentials *credentials,
+ DBusError *error)
+{
+ int bytes_read = 0;
+ DBusString buf;
+#ifdef HAVE_AFUNIX_H
+ dbus_bool_t uds = FALSE;
+#endif
+
+ char *sid = NULL;
+ dbus_pid_t pid;
+ int retval = FALSE;
+
+ // could fail due too OOM
+ if (_dbus_string_init (&buf))
+ {
+ bytes_read = _dbus_read_socket (handle, &buf, 1 );
+
+ if (bytes_read > 0)
+ _dbus_verbose ("got one zero byte from server\n");
+
+ _dbus_string_free (&buf);
+ }
+
+#ifdef HAVE_AFUNIX_H
+ uds = _dbus_socket_is_af_unix (handle, error);
+ if (dbus_error_is_set (error))
+ return FALSE;
+
+ if (uds)
+ pid = _dbus_get_peer_pid_from_uds_handle (handle.sock);
+ else
+#endif
+ pid = _dbus_get_peer_pid_from_tcp_handle (handle.sock);
+ if (pid == 0)
+ return TRUE;
+
+ _dbus_credentials_add_pid (credentials, pid);
+
+ if (_dbus_getsid (&sid, pid))
+ {
+ if (!_dbus_credentials_add_windows_sid (credentials, sid))
+ goto out;
+ }
+
+ retval = TRUE;
+
+out:
+ if (sid)
+ LocalFree (sid);
+
+ return retval;
+}
+
+/**
+* Checks to make sure the given directory is
+* private to the user
+*
+* @param dir the name of the directory
+* @param error error return
+* @returns #FALSE on failure
+**/
+dbus_bool_t
+_dbus_check_dir_is_private_to_user (DBusString *dir, DBusError *error)
+{
+ /* TODO */
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+ return TRUE;
+}
+
+
+/**
+ * Appends the given filename to the given directory.
+ *
+ * @todo it might be cute to collapse multiple '/' such as "foo//"
+ * concat "//bar"
+ *
+ * @param dir the directory name
+ * @param next_component the filename
+ * @returns #TRUE on success
+ */
+dbus_bool_t
+_dbus_concat_dir_and_file (DBusString *dir,
+ const DBusString *next_component)
+{
+ dbus_bool_t dir_ends_in_slash;
+ dbus_bool_t file_starts_with_slash;
+
+ if (_dbus_string_get_length (dir) == 0 ||
+ _dbus_string_get_length (next_component) == 0)
+ return TRUE;
+
+ dir_ends_in_slash =
+ ('/' == _dbus_string_get_byte (dir, _dbus_string_get_length (dir) - 1) ||
+ '\\' == _dbus_string_get_byte (dir, _dbus_string_get_length (dir) - 1));
+
+ file_starts_with_slash =
+ ('/' == _dbus_string_get_byte (next_component, 0) ||
+ '\\' == _dbus_string_get_byte (next_component, 0));
+
+ if (dir_ends_in_slash && file_starts_with_slash)
+ {
+ _dbus_string_shorten (dir, 1);
+ }
+ else if (!(dir_ends_in_slash || file_starts_with_slash))
+ {
+ if (!_dbus_string_append_byte (dir, '\\'))
+ return FALSE;
+ }
+
+ return _dbus_string_copy (next_component, 0, dir,
+ _dbus_string_get_length (dir));
+}
+
+/*---------------- DBusCredentials ----------------------------------*/
+
+/**
+ * Adds the credentials corresponding to the given username.
+ *
+ * @param credentials credentials to fill in
+ * @param username the username
+ * @returns #TRUE if the username existed and we got some credentials
+ */
+dbus_bool_t
+_dbus_credentials_add_from_user (DBusCredentials *credentials,
+ const DBusString *username,
+ DBusCredentialsAddFlags flags,
+ DBusError *error)
+{
+ if (!_dbus_credentials_add_windows_sid (credentials,
+ _dbus_string_get_const_data (username)))
+ {
+ _DBUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**
+ * Adds the credentials of the current process to the
+ * passed-in credentials object.
+ *
+ * @param credentials credentials to add to
+ * @returns #FALSE if no memory; does not properly roll back on failure, so only some credentials may have been added
+ */
+
+dbus_bool_t
+_dbus_credentials_add_from_current_process (DBusCredentials *credentials)
+{
+ dbus_bool_t retval = FALSE;
+ char *sid = NULL;
+
+ if (!_dbus_getsid(&sid, _dbus_getpid()))
+ goto failed;
+
+ if (!_dbus_credentials_add_pid (credentials, _dbus_getpid()))
+ goto failed;
+
+ if (!_dbus_credentials_add_windows_sid (credentials,sid))
+ goto failed;
+
+ retval = TRUE;
+ goto end;
+failed:
+ retval = FALSE;
+end:
+ if (sid)
+ LocalFree(sid);
+
+ return retval;
+}
+
+/**
+ * Append to the string the identity we would like to have when we
+ * authenticate, on UNIX this is the current process UID and on
+ * Windows something else, probably a Windows SID string. No escaping
+ * is required, that is done in dbus-auth.c. The username here
+ * need not be anything human-readable, it can be the machine-readable
+ * form i.e. a user id.
+ *
+ * @param str the string to append to
+ * @returns #FALSE on no memory
+ * @todo to which class belongs this
+ */
+dbus_bool_t
+_dbus_append_user_from_current_process (DBusString *str)
+{
+ dbus_bool_t retval = FALSE;
+ char *sid = NULL;
+
+ if (!_dbus_getsid(&sid, _dbus_getpid()))
+ return FALSE;
+
+ retval = _dbus_string_append (str,sid);
+
+ LocalFree(sid);
+ return retval;
+}
+
+/**
+ * Gets our process ID
+ * @returns process ID
+ */
+dbus_pid_t
+_dbus_getpid (void)
+{
+ return GetCurrentProcessId ();
+}
+
+/** Gets our Unix UID
+ * @returns on Windows, just DBUS_UID_UNSET
+ */
+dbus_uid_t
+_dbus_getuid (void)
+{
+ return DBUS_UID_UNSET;
+}
+
+/** nanoseconds in a second */
+#define NANOSECONDS_PER_SECOND 1000000000
+/** microseconds in a second */
+#define MICROSECONDS_PER_SECOND 1000000
+/** milliseconds in a second */
+#define MILLISECONDS_PER_SECOND 1000
+/** nanoseconds in a millisecond */
+#define NANOSECONDS_PER_MILLISECOND 1000000
+/** microseconds in a millisecond */
+#define MICROSECONDS_PER_MILLISECOND 1000
+
+/**
+ * Sleeps the given number of milliseconds.
+ * @param milliseconds number of milliseconds
+ */
+void
+_dbus_sleep_milliseconds (int milliseconds)
+{
+ Sleep (milliseconds);
+}
+
+
+/**
+ * Get current time, as in gettimeofday(). Never uses the monotonic
+ * clock.
+ *
+ * @param tv_sec return location for number of seconds
+ * @param tv_usec return location for number of microseconds
+ */
+void
+_dbus_get_real_time (dbus_int64_t *tv_sec,
+ long *tv_usec)
+{
+ FILETIME ft;
+ dbus_uint64_t time64;
+
+ GetSystemTimeAsFileTime (&ft);
+
+ memcpy (&time64, &ft, sizeof (time64));
+
+ /* Convert from 100s of nanoseconds since 1601-01-01
+ * to Unix epoch. Yes, this is Y2038 unsafe.
+ */
+ time64 -= DBUS_INT64_CONSTANT (116444736000000000);
+ time64 /= 10;
+
+ if (tv_sec)
+ *tv_sec = time64 / 1000000;
+
+ if (tv_usec)
+ *tv_usec = time64 % 1000000;
+}
+
+/**
+ * Get current time, as in gettimeofday(). Use the monotonic clock if
+ * available, to avoid problems when the system time changes.
+ *
+ * @param tv_sec return location for number of seconds
+ * @param tv_usec return location for number of microseconds
+ */
+void
+_dbus_get_monotonic_time (dbus_int64_t *tv_sec,
+ long *tv_usec)
+{
+ /* no implementation yet, fall back to wall-clock time */
+ _dbus_get_real_time (tv_sec, tv_usec);
+}
+
+/**
+ * signal (SIGPIPE, SIG_IGN);
+ */
+void
+_dbus_disable_sigpipe (void)
+{
+}
+
+/**
+ * Creates a directory. Unlike _dbus_ensure_directory(), this only succeeds
+ * if the directory is genuinely newly-created.
+ *
+ * @param filename directory filename
+ * @param error initialized error object
+ * @returns #TRUE on success
+ */
+dbus_bool_t
+_dbus_create_directory (const DBusString *filename,
+ DBusError *error)
+{
+ const char *filename_c;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ filename_c = _dbus_string_get_const_data (filename);
+
+ if (!CreateDirectoryA (filename_c, NULL))
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "Failed to create directory %s: %s\n",
+ filename_c, _dbus_strerror_from_errno ());
+ return FALSE;
+ }
+ else
+ return TRUE;
+}
+
+/**
+ * Creates a directory; succeeds if the directory
+ * is created or already existed.
+ *
+ * @param filename directory filename
+ * @param error initialized error object
+ * @returns #TRUE on success
+ */
+dbus_bool_t
+_dbus_ensure_directory (const DBusString *filename,
+ DBusError *error)
+{
+ const char *filename_c;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ filename_c = _dbus_string_get_const_data (filename);
+
+ if (!CreateDirectoryA (filename_c, NULL))
+ {
+ if (GetLastError () == ERROR_ALREADY_EXISTS)
+ return TRUE;
+
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "Failed to create directory %s: %s\n",
+ filename_c, _dbus_strerror_from_errno ());
+ return FALSE;
+ }
+ else
+ return TRUE;
+}
+
+
+/**
+ * Generates the given number of random bytes,
+ * using the best mechanism we can come up with.
+ *
+ * @param str the string
+ * @param n_bytes the number of random bytes to append to string
+ * @param error location to store reason for failure
+ * @returns #TRUE on success
+ */
+dbus_bool_t
+_dbus_generate_random_bytes (DBusString *str,
+ int n_bytes,
+ DBusError *error)
+{
+ int old_len;
+ unsigned char *p;
+ HCRYPTPROV hprov;
+
+ old_len = _dbus_string_get_length (str);
+
+ if (!_dbus_string_lengthen (str, n_bytes))
+ {
+ _DBUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ p = _dbus_string_get_udata_len (str, old_len, n_bytes);
+
+ if (!CryptAcquireContext (&hprov, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
+ {
+ _DBUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ if (!CryptGenRandom (hprov, n_bytes, p))
+ {
+ _DBUS_SET_OOM (error);
+ CryptReleaseContext (hprov, 0);
+ return FALSE;
+ }
+
+ CryptReleaseContext (hprov, 0);
+
+ return TRUE;
+}
+
+/**
+ * Gets the temporary files directory, using GetTempPath()
+ *
+ * @returns location of temp directory, or #NULL if no memory for locking
+ */
+const char*
+_dbus_get_tmpdir(void)
+{
+ /* Protected by _DBUS_LOCK_sysdeps */
+ static const char* tmpdir = NULL;
+ static char buf[1000];
+
+ if (!_DBUS_LOCK (sysdeps))
+ return NULL;
+
+ if (tmpdir == NULL)
+ {
+ unsigned char *last_slash;
+ unsigned char *p = (unsigned char *)buf;
+
+ if (!GetTempPathA (sizeof (buf), buf))
+ {
+ _dbus_warn ("GetTempPath failed");
+ _dbus_abort ();
+ }
+
+ /* Drop terminating backslash or slash */
+ last_slash = _mbsrchr (p, '\\');
+ if (last_slash > p && last_slash[1] == '\0')
+ last_slash[0] = '\0';
+ last_slash = _mbsrchr (p, '/');
+ if (last_slash > p && last_slash[1] == '\0')
+ last_slash[0] = '\0';
+
+ tmpdir = buf;
+ }
+
+ _DBUS_UNLOCK (sysdeps);
+
+ _dbus_assert(tmpdir != NULL);
+
+ return tmpdir;
+}
+
+
+/**
+ * Deletes the given file.
+ *
+ * @param filename the filename
+ * @param error error location
+ *
+ * @returns #TRUE if unlink() succeeded
+ */
+dbus_bool_t
+_dbus_delete_file (const DBusString *filename,
+ DBusError *error)
+{
+ const char *filename_c;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ filename_c = _dbus_string_get_const_data (filename);
+
+ if (DeleteFileA (filename_c) == 0)
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "Failed to delete file %s: %s\n",
+ filename_c, _dbus_strerror_from_errno ());
+ return FALSE;
+ }
+ else
+ return TRUE;
+}
+
+static dbus_uint32_t fromAscii(char ascii)
+{
+ if(ascii >= '0' && ascii <= '9')
+ return ascii - '0';
+ if(ascii >= 'A' && ascii <= 'F')
+ return ascii - 'A' + 10;
+ if(ascii >= 'a' && ascii <= 'f')
+ return ascii - 'a' + 10;
+ return 0;
+}
+
+dbus_bool_t _dbus_read_local_machine_uuid (DBusGUID *machine_id,
+ dbus_bool_t create_if_not_found,
+ DBusError *error)
+{
+#ifdef DBUS_WINCE
+ return TRUE;
+ // TODO
+#else
+ HW_PROFILE_INFOA info;
+ char *lpc = &info.szHwProfileGuid[0];
+ dbus_uint32_t u;
+
+ // the hw-profile guid lives long enough
+ if(!GetCurrentHwProfileA(&info))
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); // FIXME
+ return FALSE;
+ }
+
+ // Form: {12340001-4980-1920-6788-123456789012}
+ lpc++;
+ // 12340001
+ u = ((fromAscii(lpc[0]) << 0) |
+ (fromAscii(lpc[1]) << 4) |
+ (fromAscii(lpc[2]) << 8) |
+ (fromAscii(lpc[3]) << 12) |
+ (fromAscii(lpc[4]) << 16) |
+ (fromAscii(lpc[5]) << 20) |
+ (fromAscii(lpc[6]) << 24) |
+ (fromAscii(lpc[7]) << 28));
+ machine_id->as_uint32s[0] = u;
+
+ lpc += 9;
+ // 4980-1920
+ u = ((fromAscii(lpc[0]) << 0) |
+ (fromAscii(lpc[1]) << 4) |
+ (fromAscii(lpc[2]) << 8) |
+ (fromAscii(lpc[3]) << 12) |
+ (fromAscii(lpc[5]) << 16) |
+ (fromAscii(lpc[6]) << 20) |
+ (fromAscii(lpc[7]) << 24) |
+ (fromAscii(lpc[8]) << 28));
+ machine_id->as_uint32s[1] = u;
+
+ lpc += 10;
+ // 6788-1234
+ u = ((fromAscii(lpc[0]) << 0) |
+ (fromAscii(lpc[1]) << 4) |
+ (fromAscii(lpc[2]) << 8) |
+ (fromAscii(lpc[3]) << 12) |
+ (fromAscii(lpc[5]) << 16) |
+ (fromAscii(lpc[6]) << 20) |
+ (fromAscii(lpc[7]) << 24) |
+ (fromAscii(lpc[8]) << 28));
+ machine_id->as_uint32s[2] = u;
+
+ lpc += 9;
+ // 56789012
+ u = ((fromAscii(lpc[0]) << 0) |
+ (fromAscii(lpc[1]) << 4) |
+ (fromAscii(lpc[2]) << 8) |
+ (fromAscii(lpc[3]) << 12) |
+ (fromAscii(lpc[4]) << 16) |
+ (fromAscii(lpc[5]) << 20) |
+ (fromAscii(lpc[6]) << 24) |
+ (fromAscii(lpc[7]) << 28));
+ machine_id->as_uint32s[3] = u;
+#endif
+ return TRUE;
+}
+
+// for proper cleanup in dbus-daemon
+static HANDLE hDBusDaemonMutex = NULL;
+static HANDLE hDBusSharedMem = NULL;
+// sync _dbus_daemon_publish_session_bus_address, _dbus_daemon_unpublish_session_bus_address and _dbus_daemon_already_runs
+static const char *cUniqueDBusInitMutex = "UniqueDBusInitMutex";
+// sync _dbus_get_autolaunch_address
+static const char *cDBusAutolaunchMutex = "DBusAutolaunchMutex";
+// mutex to determine if dbus-daemon is already started (per user)
+static const char *cDBusDaemonMutex = "DBusDaemonMutex";
+// named shm for dbus adress info (per user)
+static const char *cDBusDaemonAddressInfo = "DBusDaemonAddressInfo";
+
+/* custom command line parameter for autolaunching daemon */
+static const char *autolaunch_custom_command_line_parameter = "";
+
+/**
+ * Set command line parameters for the dbus daemon to start
+ * for an autolaunch session.
+ *
+ * The specified instance must be valid until the dbus-daemon
+ * is started.
+ *
+ * This function is not thread-safe, and can only be called from a
+ * single-threaded unit test.
+ *
+ * @param path string to use as command line parameter
+ */
+void _dbus_test_win_autolaunch_set_command_line_parameter (const char *path)
+{
+ autolaunch_custom_command_line_parameter = path;
+}
+
+static HANDLE *autolaunch_handle_location;
+
+/**
+ * Set location where to store process handle of an autostarted server
+ *
+ * This function is not thread-safe, and can only be called from a
+ * single-threaded unit test.
+ *
+ * After using the handle it must be closed with @ref CloseHandle().
+ *
+ * @param location Pointer where to store the handle
+ */
+void
+_dbus_test_win_set_autolaunch_handle_location (HANDLE *location)
+{
+ autolaunch_handle_location = location;
+}
+
+/**
+ * Return the hash of the installation root directory, which can be
+ * used to construct a per-installation-root scope for autolaunching
+ *
+ * If the installation root directory could not be
+ * determined, the returned length is set to zero.
+ *
+ * @param out initialized DBusString instance to return hash string
+ * @returns #FALSE on OOM, #TRUE if not OOM
+ */
+static dbus_bool_t
+_dbus_get_install_root_as_hash (DBusString *out)
+{
+ DBusString install_path;
+ dbus_bool_t retval = FALSE;
+ _dbus_assert (out != NULL);
+
+ if (!_dbus_string_init (&install_path))
+ return FALSE;
+
+ if (!_dbus_get_install_root (&install_path))
+ goto out;
+
+ /* the install path can't be determined */
+ if (_dbus_string_get_length (&install_path) == 0)
+ {
+ _dbus_string_set_length (out, 0);
+ retval = TRUE;
+ goto out;
+ }
+
+ _dbus_string_tolower_ascii (&install_path, 0, _dbus_string_get_length (&install_path));
+
+ if (!_dbus_sha_compute (&install_path, out))
+ goto out;
+
+ retval = TRUE;
+
+out:
+ _dbus_string_free (&install_path);
+ return retval;
+}
+
+/**
+ * Build a name from \p basestring and \p scope, and append it to \p out
+ *
+ * The name will be suitable for naming Windows objects such as mutexes
+ * and shared memory segments that need to be unique for each distinct
+ * \p scope, but shared between clients with the same \p scope.
+ *
+ * If \p scope has one of the special values recognised in autolaunch:
+ * addresses on Windows, substitute a unique string based on the scope
+ * (the username or the hash of the installation path) instead of the
+ * literal scope itself.
+ *
+ * With the '*install-path' \p scope the returned length can be zero,
+ * indicating that the name could not be determined.
+ *
+ * @param out initialized DBusString instance to return bus address
+ * @returns #FALSE on OOM, #TRUE if not OOM
+ */
+static dbus_bool_t
+_dbus_get_address_string (DBusString *out, const char *basestring, const char *scope)
+{
+ _dbus_assert (out != NULL);
+
+ if (!scope || strlen (scope) == 0)
+ {
+ return _dbus_string_append (out, basestring);
+ }
+ else if (strcmp (scope, "*install-path") == 0
+ // for 1.3 compatibility
+ || strcmp (scope, "install-path") == 0)
+ {
+ DBusString temp;
+ dbus_bool_t retval = FALSE;
+
+ if (!_dbus_string_init (&temp))
+ return FALSE;
+
+ if (!_dbus_get_install_root_as_hash (&temp))
+ goto out;
+
+ if (_dbus_string_get_length (&temp) == 0)
+ {
+ _dbus_string_set_length (out, 0);
+ retval = TRUE;
+ goto out;
+ }
+
+ if (!_dbus_string_append_printf (out, "%s-%s", basestring, _dbus_string_get_const_data (&temp)))
+ goto out;
+
+ retval = TRUE;
+out:
+ _dbus_string_free (&temp);
+ return retval;
+ }
+ else if (strcmp (scope, "*user") == 0)
+ {
+ char *sid = NULL;
+ dbus_bool_t retval;
+
+ if (!_dbus_getsid (&sid, _dbus_getpid()))
+ return FALSE;
+
+ retval = _dbus_string_append_printf (out, "%s-%s", basestring, sid);
+
+ LocalFree(sid);
+
+ return retval;
+ }
+ else /* strlen(scope) > 0 */
+ {
+ return _dbus_string_append_printf (out, "%s-%s", basestring, scope);
+ }
+}
+
+/**
+ * Return name of shared memory segment constructed from the autolaunch scope \p scope
+ *
+ * See @ref _dbus_get_address_string for further usage information.
+ *
+ * @param out initialized DBusString instance to return shared memory segment name
+ * @returns #FALSE on OOM, #TRUE if not OOM
+ */
+static dbus_bool_t
+_dbus_get_shm_name (DBusString *out,const char *scope)
+{
+ return _dbus_get_address_string (out, cDBusDaemonAddressInfo, scope);
+}
+
+/**
+ * Return mutex name for scope \p scope in \p out
+ *
+ * See @ref _dbus_get_address_string for further usage information.
+ *
+ * @param out initialized DBusString instance to return mutex name
+ * @param scope scope for the requested mutex name
+ * @returns #FALSE on OOM, #TRUE if not OOM
+ */
+static dbus_bool_t
+_dbus_get_mutex_name (DBusString *out, const char *scope)
+{
+ return _dbus_get_address_string (out, cDBusDaemonMutex, scope);
+}
+
+dbus_bool_t
+_dbus_daemon_is_session_bus_address_published (const char *scope)
+{
+ DBusRMutex *lock = NULL;
+ DBusString mutex_name;
+
+ if (!_dbus_string_init (&mutex_name))
+ return FALSE;
+
+ _dbus_verbose ("scope:%s\n", scope);
+ if (!_dbus_get_mutex_name (&mutex_name, scope) ||
+ /* not determinable */
+ _dbus_string_get_length (&mutex_name) == 0)
+ {
+ _dbus_string_free (&mutex_name);
+ return FALSE;
+ }
+
+ if (hDBusDaemonMutex)
+ {
+ _dbus_verbose ("(scope:%s) -> yes\n", scope);
+ return TRUE;
+ }
+ lock = _dbus_win_rmutex_named_new (cUniqueDBusInitMutex);
+ if (!lock)
+ return FALSE;
+
+ // sync _dbus_daemon_publish_session_bus_address, _dbus_daemon_unpublish_session_bus_address and _dbus_daemon_already_runs
+ _dbus_platform_rmutex_lock (lock);
+
+ // we use CreateMutex instead of OpenMutex because of possible race conditions,
+ // see http://msdn.microsoft.com/en-us/library/ms684315%28VS.85%29.aspx
+ hDBusDaemonMutex = CreateMutexA (NULL, FALSE, _dbus_string_get_const_data(&mutex_name));
+
+ /* The client uses mutex ownership to detect a running server, so the server should do so too.
+ Fortunally the client deletes the mutex in the lock protected area, so checking presence
+ will work too. */
+
+ _dbus_platform_rmutex_unlock (lock);
+ _dbus_platform_rmutex_free (lock);
+
+ _dbus_string_free (&mutex_name);
+
+ if (hDBusDaemonMutex == NULL)
+ {
+ _dbus_verbose ("(scope:%s) -> no\n", scope);
+ return FALSE;
+ }
+ if (GetLastError() == ERROR_ALREADY_EXISTS)
+ {
+ CloseHandle(hDBusDaemonMutex);
+ hDBusDaemonMutex = NULL;
+ _dbus_verbose ("(scope:%s) -> yes\n", scope);
+ return TRUE;
+ }
+ // mutex wasn't created before, so return false.
+ // We leave the mutex name allocated for later reusage
+ // in _dbus_daemon_publish_session_bus_address.
+ _dbus_verbose ("(scope:%s) -> no\n", scope);
+ return FALSE;
+}
+
+dbus_bool_t
+_dbus_daemon_publish_session_bus_address (const char* address, const char *scope)
+{
+ DBusRMutex *lock = NULL;
+ char *shared_addr = NULL;
+ DBusString shm_name = _DBUS_STRING_INIT_INVALID;
+ DBusString mutex_name;
+ dbus_uint64_t len;
+ dbus_bool_t retval = FALSE;
+
+ _dbus_assert (address);
+
+ if (!_dbus_string_init (&mutex_name))
+ return FALSE;
+
+ _dbus_verbose ("address:%s scope:%s\n", address, scope);
+ if (!_dbus_get_mutex_name (&mutex_name, scope) ||
+ /* not determinable */
+ _dbus_string_get_length (&mutex_name) == 0)
+ {
+ _dbus_string_free (&mutex_name);
+ return FALSE;
+ }
+
+ // sync _dbus_daemon_publish_session_bus_address, _dbus_daemon_unpublish_session_bus_address and _dbus_daemon_already_runs
+ lock = _dbus_win_rmutex_named_new (cUniqueDBusInitMutex);
+ if (lock == NULL)
+ {
+ _dbus_string_free (&mutex_name);
+ return FALSE;
+ }
+
+ _dbus_platform_rmutex_lock (lock);
+
+ if (!hDBusDaemonMutex)
+ {
+ hDBusDaemonMutex = CreateMutexA (NULL, FALSE, _dbus_string_get_const_data(&mutex_name));
+ }
+ _dbus_string_free (&mutex_name);
+
+ // acquire the mutex
+ if (WaitForSingleObject (hDBusDaemonMutex, 10) != WAIT_OBJECT_0)
+ {
+ CloseHandle (hDBusDaemonMutex);
+ goto out;
+ }
+
+ if (!_dbus_string_init (&shm_name))
+ {
+ goto out;
+ }
+
+ if (!_dbus_get_shm_name (&shm_name, scope) ||
+ /* not determinable */
+ _dbus_string_get_length (&shm_name) == 0)
+ {
+ goto out;
+ }
+
+ // create shm
+ len = strlen (address) + 1;
+
+ hDBusSharedMem = CreateFileMappingA ( INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE,
+ len >> 32, len & 0xffffffffu,
+ _dbus_string_get_const_data (&shm_name) );
+ _dbus_assert (hDBusSharedMem);
+
+ shared_addr = MapViewOfFile (hDBusSharedMem, FILE_MAP_WRITE, 0, 0, 0);
+
+ _dbus_assert (shared_addr);
+
+ strcpy(shared_addr, address);
+
+ // cleanup
+ UnmapViewOfFile (shared_addr);
+
+ _dbus_verbose ("published session bus address at %s\n",_dbus_string_get_const_data (&shm_name));
+ retval = TRUE;
+
+out:
+ _dbus_platform_rmutex_unlock (lock);
+ _dbus_platform_rmutex_free (lock);
+ _dbus_string_free (&shm_name);
+ return retval;
+}
+
+/**
+ * Clear the platform-specific centralized location where the session
+ * bus address is published.
+ *
+ * This must only be called if \ref DBusServer.published_address is #TRUE,
+ * which is be the case if and only if platform-specific code has published
+ * the address centrally.
+ *
+ * On Windows, this is implemented by closing a global shared memory segment.
+ *
+ * On Unix, the session bus address is not published in a centralized
+ * location by libdbus, so this function does nothing. The closest
+ * equivalent on Unix is that the session bus address is published by the
+ * dbus-launch tool, and unpublished automatically when the dbus-launch
+ * tool exits.
+ * @return NULL in case of error
+ */
+dbus_bool_t
+_dbus_daemon_unpublish_session_bus_address (void)
+{
+ DBusRMutex *lock = NULL;
+
+ _dbus_verbose ("\n");
+ // sync _dbus_daemon_publish_session_bus_address, _dbus_daemon_unpublish_session_bus_address and _dbus_daemon_already_runs
+ lock = _dbus_win_rmutex_named_new (cUniqueDBusInitMutex);
+ if (lock == NULL)
+ return FALSE;
+
+ _dbus_platform_rmutex_lock (lock);
+
+ CloseHandle (hDBusSharedMem);
+
+ hDBusSharedMem = NULL;
+
+ ReleaseMutex (hDBusDaemonMutex);
+
+ CloseHandle (hDBusDaemonMutex);
+
+ hDBusDaemonMutex = NULL;
+
+ _dbus_platform_rmutex_unlock (lock);
+ _dbus_platform_rmutex_free (lock);
+ return TRUE;
+}
+
+/**
+ * Get server bus address from shared memory segment provided by running dbus-daemon
+ *
+ * @param address initialized DBusString instance to store the retrieved address
+ * @param shm_name the name of the shared memory segment
+ * @param wait if TRUE wait maximum 2 seconds for the presence of the shared memory segment
+ * @return #TRUE the bus address was fetched from the shared memory segment
+ * @return #FALSE error during execution
+ */
+static dbus_bool_t
+_dbus_get_autolaunch_shm (DBusString *address, DBusString *shm_name, dbus_bool_t wait)
+{
+ HANDLE sharedMem = NULL;
+ char *shared_addr;
+ int i;
+ int max = 20; /* max 2 seconds */
+ dbus_bool_t retval = FALSE;
+
+ if (!wait)
+ max = 1;
+
+ // read shm
+ for (i = 0; i < max; ++i)
+ {
+ // we know that dbus-daemon is available, so we wait until shm is available
+ sharedMem = OpenFileMappingA (FILE_MAP_READ, FALSE, _dbus_string_get_const_data (shm_name));
+ if (sharedMem == 0)
+ Sleep (100);
+ if (sharedMem != 0)
+ break;
+ }
+
+ if (sharedMem == 0)
+ return FALSE;
+
+ shared_addr = MapViewOfFile (sharedMem, FILE_MAP_READ, 0, 0, 0);
+
+ if (!shared_addr)
+ goto out;
+
+ retval = _dbus_string_append (address, shared_addr);
+
+ UnmapViewOfFile (shared_addr);
+
+out:
+ CloseHandle (sharedMem);
+ return retval;
+}
+
+static dbus_bool_t
+_dbus_daemon_already_runs (DBusString *address, DBusString *shm_name, const char *scope)
+{
+ DBusRMutex *lock = NULL;
+ HANDLE daemon;
+ DBusString mutex_name;
+ dbus_bool_t retval = FALSE;
+
+ if (!_dbus_string_init (&mutex_name))
+ return FALSE;
+
+ if (!_dbus_get_mutex_name (&mutex_name, scope) ||
+ /* not determinable */
+ _dbus_string_get_length (&mutex_name) == 0)
+ {
+ _dbus_string_free (&mutex_name);
+ return FALSE;
+ }
+
+ // sync _dbus_daemon_publish_session_bus_address, _dbus_daemon_unpublish_session_bus_address and _dbus_daemon_already_runs
+ lock = _dbus_win_rmutex_named_new (cUniqueDBusInitMutex);
+ if (lock == NULL)
+ return FALSE;
+
+ _dbus_platform_rmutex_lock (lock);
+
+ // do checks
+ daemon = CreateMutexA (NULL, FALSE, _dbus_string_get_const_data (&mutex_name));
+ if (WaitForSingleObject (daemon, 10) != WAIT_TIMEOUT)
+ {
+ ReleaseMutex (daemon);
+ CloseHandle (daemon);
+ goto out;
+ }
+
+ // read shm, wait max 2 seconds
+ retval = _dbus_get_autolaunch_shm (address, shm_name, TRUE);
+
+ // cleanup
+ CloseHandle (daemon);
+
+out:
+ _dbus_platform_rmutex_unlock (lock);
+ _dbus_platform_rmutex_free (lock);
+ _dbus_string_free (&mutex_name);
+
+ return retval;
+}
+
+dbus_bool_t
+_dbus_get_autolaunch_address (const char *scope,
+ DBusString *address,
+ DBusError *error)
+{
+ DBusRMutex *lock = NULL;
+ STARTUPINFOA si;
+ PROCESS_INFORMATION pi;
+ dbus_bool_t retval = FALSE;
+ LPSTR lpFile;
+ char dbus_exe_path[MAX_PATH];
+ DBusString dbus_args = _DBUS_STRING_INIT_INVALID;
+ const char *daemon_name = DBUS_DAEMON_NAME ".exe";
+ DBusString shm_name;
+ HANDLE ready_event_handle = NULL;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ if (!_dbus_string_init (&shm_name))
+ {
+ _DBUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ if (!_dbus_get_shm_name (&shm_name, scope) ||
+ /* not determinable */
+ _dbus_string_get_length (&shm_name) == 0)
+ {
+ dbus_set_error_const (error, DBUS_ERROR_FAILED, "could not determine shm name");
+ goto out;
+ }
+
+ lock = _dbus_win_rmutex_named_new (cDBusAutolaunchMutex);
+ if (lock == NULL)
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED, "Could not lock '%s'", cDBusAutolaunchMutex);
+ _dbus_string_free (&shm_name);
+ return FALSE;
+ }
+
+ _dbus_platform_rmutex_lock (lock);
+
+ if (_dbus_daemon_already_runs (address, &shm_name, scope))
+ {
+ _dbus_verbose ("found running dbus daemon for scope '%s' at %s\n",
+ scope ? scope : "", _dbus_string_get_const_data (&shm_name));
+ retval = TRUE;
+ goto out;
+ }
+
+ if (!SearchPathA (NULL, daemon_name, NULL, sizeof (dbus_exe_path), dbus_exe_path, &lpFile))
+ {
+ // Look in directory containing dbus shared library
+ HMODULE hmod;
+ char dbus_module_path[MAX_PATH];
+ DWORD rc;
+
+ _dbus_verbose ("did not found dbus daemon executable on default search path, "
+ "trying path where dbus shared library is located");
+
+ hmod = _dbus_win_get_dll_hmodule ();
+ rc = GetModuleFileNameA (hmod, dbus_module_path, sizeof (dbus_module_path));
+ if (rc <= 0)
+ {
+ dbus_set_error_const (error, DBUS_ERROR_FAILED, "could not retrieve dbus shared library file name");
+ retval = FALSE;
+ goto out;
+ }
+ else
+ {
+ char *ext_idx = strrchr (dbus_module_path, '\\');
+ if (ext_idx)
+ *ext_idx = '\0';
+ if (!SearchPathA (dbus_module_path, daemon_name, NULL, sizeof (dbus_exe_path), dbus_exe_path, &lpFile))
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "Could not find dbus-daemon executable. "
+ "Please add the path to %s to your PATH "
+ "environment variable or start the daemon manually",
+ daemon_name);
+ retval = FALSE;
+ goto out;
+ }
+ _dbus_verbose ("found dbus daemon executable at %s", dbus_module_path);
+ }
+ }
+
+ // Create process
+ ZeroMemory (&si, sizeof (si));
+ si.cb = sizeof (si);
+ ZeroMemory (&pi, sizeof (pi));
+
+ if (!_dbus_string_init (&dbus_args))
+ {
+ dbus_set_error_const (error, DBUS_ERROR_NO_MEMORY, "Failed to initialize argument buffer");
+ retval = FALSE;
+ goto out;
+ }
+
+ if (!_dbus_string_append_printf (&dbus_args, "\"%s\" %s", dbus_exe_path,
+ autolaunch_custom_command_line_parameter ? autolaunch_custom_command_line_parameter : "--session"))
+ {
+ _DBUS_SET_OOM (error);
+ retval = FALSE;
+ goto out;
+ }
+
+ ready_event_handle = _dbus_win_event_create_inheritable (error);
+ if (ready_event_handle == NULL)
+ goto out;
+
+ _dbus_verbose ("Creating connection readiness event: handle=%p\n", ready_event_handle);
+ if (!_dbus_string_append_printf (&dbus_args, " \"--ready-event-handle=%p\"", ready_event_handle))
+ {
+ _DBUS_SET_OOM (error);
+ goto out;
+ }
+
+ _dbus_verbose ("Starting dbus daemon with args: '%s'\n", _dbus_string_get_const_data (&dbus_args));
+ if (CreateProcessA (dbus_exe_path, _dbus_string_get_data (&dbus_args), NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi))
+ {
+ DWORD status;
+ HANDLE events[2];
+
+ CloseHandle (pi.hThread);
+
+ _dbus_verbose ("Wait until dbus-daemon is ready for connections (event handle %p)\n", ready_event_handle);
+
+ events[0] = ready_event_handle;
+ events[1] = pi.hProcess;
+ status = WaitForMultipleObjects (2, events, FALSE, 30000);
+
+ switch (status)
+ {
+ case WAIT_OBJECT_0:
+ /* ready event signalled, everything is okay */
+ retval = TRUE;
+ break;
+
+ case WAIT_OBJECT_0 + 1:
+ /* dbus-daemon process has exited */
+ dbus_set_error (error, DBUS_ERROR_SPAWN_CHILD_EXITED, "dbus-daemon exited before signalling ready");
+ goto out;
+
+ case WAIT_FAILED:
+ _dbus_win_set_error_from_last_error (error, "Unable to wait for server readiness (handle %p)", ready_event_handle);
+ goto out;
+
+ case WAIT_TIMEOUT:
+ /* GetLastError() is not set */
+ dbus_set_error (error, DBUS_ERROR_TIMEOUT, "Timed out waiting for server readiness or exit (handle %p)", ready_event_handle);
+ goto out;
+
+ default:
+ /* GetLastError() is probably not set? */
+ dbus_set_error (error, DBUS_ERROR_FAILED, "Unknown result '%lu' while waiting for server readiness (handle %p)", status, ready_event_handle);
+ goto out;
+ }
+ _dbus_verbose ("Got signal that dbus-daemon with process id '%ld' is ready for connections\n", GetProcessId (pi.hProcess));
+
+ if (autolaunch_handle_location != NULL)
+ {
+ *autolaunch_handle_location = pi.hProcess;
+ _dbus_verbose ("Returning process handle of started server (handle=%p)\n", pi.hProcess);
+ }
+ else
+ {
+ CloseHandle (pi.hProcess);
+ }
+
+ /* do not wait for the appearance of shm, we can assume that it is present */
+ retval = _dbus_get_autolaunch_shm (address, &shm_name, FALSE);
+ if (retval == FALSE)
+ dbus_set_error_const (error, DBUS_ERROR_FAILED, "Failed to get autolaunch address from launched dbus-daemon");
+ }
+ else
+ {
+ dbus_set_error_const (error, DBUS_ERROR_FAILED, "Failed to launch dbus-daemon");
+ retval = FALSE;
+ }
+
+out:
+ _DBUS_ASSERT_ERROR_XOR_BOOL (error, retval);
+ _dbus_platform_rmutex_unlock (lock);
+ _dbus_platform_rmutex_free (lock);
+ _dbus_string_free (&shm_name);
+ _dbus_string_free (&dbus_args);
+ if (ready_event_handle)
+ _dbus_win_event_free (ready_event_handle, NULL);
+
+ _DBUS_ASSERT_ERROR_XOR_BOOL (error, retval);
+ return retval;
+}
+
+/** Makes the file readable by every user in the system.
+ *
+ * @param filename the filename
+ * @param error error location
+ * @returns #TRUE if the file's permissions could be changed.
+ */
+dbus_bool_t
+_dbus_make_file_world_readable(const DBusString *filename,
+ DBusError *error)
+{
+ // TODO
+ return TRUE;
+}
+
+/**
+ * Atomically increments an integer
+ *
+ * @param atomic pointer to the integer to increment
+ * @returns the value before incrementing
+ *
+ */
+dbus_int32_t
+_dbus_atomic_inc (DBusAtomic *atomic)
+{
+#ifdef HAVE_STDATOMIC_H
+ /* Atomic version of "old = *atomic; *atomic += 1; return old" */
+ return atomic_fetch_add (&atomic->value, 1);
+#else
+ /* Atomic version of "*atomic += 1; return *atomic - 1" */
+ return InterlockedIncrement (&atomic->value) - 1;
+#endif
+}
+
+/**
+ * Atomically decrement an integer
+ *
+ * @param atomic pointer to the integer to decrement
+ * @returns the value before decrementing
+ *
+ */
+dbus_int32_t
+_dbus_atomic_dec (DBusAtomic *atomic)
+{
+#ifdef HAVE_STDATOMIC_H
+ /* Atomic version of "old = *atomic; *atomic -= 1; return old" */
+ return atomic_fetch_sub (&atomic->value, 1);
+#else
+ /* Atomic version of "*atomic -= 1; return *atomic + 1" */
+ return InterlockedDecrement (&atomic->value) + 1;
+#endif
+}
+
+/**
+ * Atomically get the value of an integer. It may change at any time
+ * thereafter, so this is mostly only useful for assertions.
+ *
+ * @param atomic pointer to the integer to get
+ * @returns the value at this moment
+ */
+dbus_int32_t
+_dbus_atomic_get (DBusAtomic *atomic)
+{
+#ifdef HAVE_STDATOMIC_H
+ /* Atomic version of "return *atomic" */
+ return atomic_load (&atomic->value);
+#else
+ /* In this situation, GLib issues a MemoryBarrier() and then returns
+ * atomic->value. However, mingw from mingw.org (not to be confused with
+ * mingw-w64 from mingw-w64.sf.net) does not have MemoryBarrier in its
+ * headers, so we have to get a memory barrier some other way.
+ *
+ * InterlockedIncrement is older, and is documented on MSDN to be a full
+ * memory barrier, so let's use that.
+ */
+ long dummy = 0;
+
+ InterlockedExchange (&dummy, 1);
+
+ return atomic->value;
+#endif
+}
+
+/**
+ * Atomically set the value of an integer to 0.
+ *
+ * @param atomic pointer to the integer to set
+ */
+void
+_dbus_atomic_set_zero (DBusAtomic *atomic)
+{
+#ifdef HAVE_STDATOMIC_H
+ /* Atomic version of "*atomic = 0" */
+ atomic_store (&atomic->value, 0);
+#else
+ InterlockedExchange (&atomic->value, 0);
+#endif
+}
+
+/**
+ * Atomically set the value of an integer to something nonzero.
+ *
+ * @param atomic pointer to the integer to set
+ */
+void
+_dbus_atomic_set_nonzero (DBusAtomic *atomic)
+{
+#ifdef HAVE_STDATOMIC_H
+ /* Atomic version of "*atomic = 1" */
+ atomic_store (&atomic->value, 1);
+#else
+ InterlockedExchange (&atomic->value, 1);
+#endif
+}
+
+/**
+ * Called when the bus daemon is signaled to reload its configuration; any
+ * caches should be nuked. Of course any caches that need explicit reload
+ * are probably broken, but c'est la vie.
+ *
+ *
+ */
+void
+_dbus_flush_caches (void)
+{
+}
+
+/**
+ * See if errno is EAGAIN or EWOULDBLOCK (this has to be done differently
+ * for Winsock so is abstracted)
+ *
+ * @returns #TRUE if e == EAGAIN or e == EWOULDBLOCK
+ */
+dbus_bool_t
+_dbus_get_is_errno_eagain_or_ewouldblock (int e)
+{
+ return e == WSAEWOULDBLOCK;
+}
+
+/**
+ * Fill str with the absolute path of the D-Bus installation, or truncate str
+ * to zero length if we cannot determine it.
+ *
+ * @param str buffer for installation path
+ * @returns #FALSE on OOM, #TRUE if not OOM
+ */
+dbus_bool_t
+_dbus_get_install_root (DBusString *str)
+{
+ /* this is just an initial guess */
+ DWORD pathLength = MAX_PATH;
+ unsigned char *lastSlash;
+ unsigned char *prefix;
+
+ do
+ {
+ /* allocate enough space for our best guess at the length */
+ if (!_dbus_string_set_length (str, pathLength))
+ {
+ _dbus_string_set_length (str, 0);
+ return FALSE;
+ }
+
+ SetLastError (0);
+ pathLength = GetModuleFileNameA (_dbus_win_get_dll_hmodule (),
+ _dbus_string_get_data (str), _dbus_string_get_length (str));
+
+ if (pathLength == 0 || GetLastError () != 0)
+ {
+ /* failed, but not OOM */
+ _dbus_string_set_length (str, 0);
+ return TRUE;
+ }
+
+ /* if the return is strictly less than the buffer size, it has
+ * not been truncated, so we can continue */
+ if (pathLength < (DWORD) _dbus_string_get_length (str))
+ {
+ /* reduce the length to match what Windows filled in */
+ if (!_dbus_string_set_length (str, pathLength))
+ {
+ _dbus_string_set_length (str, 0);
+ return FALSE;
+ }
+
+ break;
+ }
+
+ /* else it may have been truncated; try with a larger buffer */
+ pathLength *= 2;
+ }
+ while (TRUE);
+
+ /* the rest of this function works by direct byte manipulation of the
+ * underlying buffer */
+ prefix = _dbus_string_get_udata (str);
+
+ lastSlash = _mbsrchr (prefix, '\\');
+ if (lastSlash == NULL) {
+ /* failed, but not OOM */
+ _dbus_string_set_length (str, 0);
+ return TRUE;
+ }
+ //cut off binary name
+ lastSlash[1] = 0;
+
+ //cut possible "\\bin"
+ //this fails if we are in a double-byte system codepage and the
+ //folder's name happens to end with the *bytes*
+ //"\\bin"... (I.e. the second byte of some Han character and then
+ //the Latin "bin", but that is not likely I think...
+ if (lastSlash - prefix >= 4 && _mbsnicmp (lastSlash - 4, (const unsigned char *)"\\bin", 4) == 0)
+ lastSlash[-3] = 0;
+ else if (lastSlash - prefix >= 10 && _mbsnicmp (lastSlash - 10, (const unsigned char *)"\\bin\\debug", 10) == 0)
+ lastSlash[-9] = 0;
+ else if (lastSlash - prefix >= 12 && _mbsnicmp (lastSlash - 12, (const unsigned char *)"\\bin\\release", 12) == 0)
+ lastSlash[-11] = 0;
+
+ /* fix up the length to match the byte-manipulation */
+ _dbus_string_set_length (str, strlen ((char *) prefix));
+
+ return TRUE;
+}
+
+/* See comment in dbus-sysdeps-unix.c */
+dbus_bool_t
+_dbus_lookup_session_address (dbus_bool_t *supported,
+ DBusString *address,
+ DBusError *error)
+{
+ /* Probably fill this in with something based on COM? */
+ *supported = FALSE;
+ return TRUE;
+}
+
+/**
+ * Appends the directory in which a keyring for the given credentials
+ * should be stored. The credentials should have either a Windows or
+ * UNIX user in them. The directory should be an absolute path.
+ *
+ * On UNIX the directory is ~/.dbus-keyrings while on Windows it should probably
+ * be something else, since the dotfile convention is not normal on Windows.
+ *
+ * @param directory string to append directory to
+ * @param credentials credentials the directory should be for
+ *
+ * @returns #FALSE on no memory
+ */
+dbus_bool_t
+_dbus_append_keyring_directory_for_credentials (DBusString *directory,
+ DBusCredentials *credentials)
+{
+ DBusString homedir;
+ DBusString dotdir;
+ const char *homepath;
+ const char *homedrive;
+
+ _dbus_assert (credentials != NULL);
+ _dbus_assert (!_dbus_credentials_are_anonymous (credentials));
+
+ if (!_dbus_string_init (&homedir))
+ return FALSE;
+
+ homedrive = _dbus_getenv("HOMEDRIVE");
+ if (homedrive != NULL && *homedrive != '\0')
+ {
+ _dbus_string_append(&homedir,homedrive);
+ }
+
+ homepath = _dbus_getenv("HOMEPATH");
+ if (homepath != NULL && *homepath != '\0')
+ {
+ _dbus_string_append(&homedir,homepath);
+ }
+
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
+ {
+ const char *override;
+
+ override = _dbus_getenv ("DBUS_TEST_HOMEDIR");
+ if (override != NULL && *override != '\0')
+ {
+ _dbus_string_set_length (&homedir, 0);
+ if (!_dbus_string_append (&homedir, override))
+ goto failed;
+
+ _dbus_verbose ("Using fake homedir for testing: %s\n",
+ _dbus_string_get_const_data (&homedir));
+ }
+ else
+ {
+ /* Not strictly thread-safe, but if we fail at thread-safety here,
+ * the worst that will happen is some extra warnings. */
+ static dbus_bool_t already_warned = FALSE;
+ if (!already_warned)
+ {
+ _dbus_warn ("Using your real home directory for testing, set DBUS_TEST_HOMEDIR to avoid");
+ already_warned = TRUE;
+ }
+ }
+ }
+#endif
+
+#ifdef DBUS_WINCE
+ /* It's not possible to create a .something directory in Windows CE
+ using the file explorer. */
+#define KEYRING_DIR "dbus-keyrings"
+#else
+#define KEYRING_DIR ".dbus-keyrings"
+#endif
+
+ _dbus_string_init_const (&dotdir, KEYRING_DIR);
+ if (!_dbus_concat_dir_and_file (&homedir,
+ &dotdir))
+ goto failed;
+
+ if (!_dbus_string_copy (&homedir, 0,
+ directory, _dbus_string_get_length (directory))) {
+ goto failed;
+ }
+
+ _dbus_string_free (&homedir);
+ return TRUE;
+
+ failed:
+ _dbus_string_free (&homedir);
+ return FALSE;
+}
+
+/** Checks if a file exists
+*
+* @param file full path to the file
+* @returns #TRUE if file exists
+*/
+dbus_bool_t
+_dbus_file_exists (const char *file)
+{
+ DWORD attributes = GetFileAttributesA (file);
+
+ if (attributes != INVALID_FILE_ATTRIBUTES && GetLastError() != ERROR_PATH_NOT_FOUND)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+/**
+ * A wrapper around strerror() because some platforms
+ * may be lame and not have strerror().
+ *
+ * @param error_number errno.
+ * @returns error description.
+ */
+const char*
+_dbus_strerror (int error_number)
+{
+#ifdef DBUS_WINCE
+ // TODO
+ return "unknown";
+#else
+ const char *msg;
+
+ switch (error_number)
+ {
+ case WSAEINTR:
+ return "Interrupted function call";
+ case WSAEACCES:
+ return "Permission denied";
+ case WSAEFAULT:
+ return "Bad address";
+ case WSAEINVAL:
+ return "Invalid argument";
+ case WSAEMFILE:
+ return "Too many open files";
+ case WSAEWOULDBLOCK:
+ return "Resource temporarily unavailable";
+ case WSAEINPROGRESS:
+ return "Operation now in progress";
+ case WSAEALREADY:
+ return "Operation already in progress";
+ case WSAENOTSOCK:
+ return "Socket operation on nonsocket";
+ case WSAEDESTADDRREQ:
+ return "Destination address required";
+ case WSAEMSGSIZE:
+ return "Message too long";
+ case WSAEPROTOTYPE:
+ return "Protocol wrong type for socket";
+ case WSAENOPROTOOPT:
+ return "Bad protocol option";
+ case WSAEPROTONOSUPPORT:
+ return "Protocol not supported";
+ case WSAESOCKTNOSUPPORT:
+ return "Socket type not supported";
+ case WSAEOPNOTSUPP:
+ return "Operation not supported";
+ case WSAEPFNOSUPPORT:
+ return "Protocol family not supported";
+ case WSAEAFNOSUPPORT:
+ return "Address family not supported by protocol family";
+ case WSAEADDRINUSE:
+ return "Address already in use";
+ case WSAEADDRNOTAVAIL:
+ return "Cannot assign requested address";
+ case WSAENETDOWN:
+ return "Network is down";
+ case WSAENETUNREACH:
+ return "Network is unreachable";
+ case WSAENETRESET:
+ return "Network dropped connection on reset";
+ case WSAECONNABORTED:
+ return "Software caused connection abort";
+ case WSAECONNRESET:
+ return "Connection reset by peer";
+ case WSAENOBUFS:
+ return "No buffer space available";
+ case WSAEISCONN:
+ return "Socket is already connected";
+ case WSAENOTCONN:
+ return "Socket is not connected";
+ case WSAESHUTDOWN:
+ return "Cannot send after socket shutdown";
+ case WSAETIMEDOUT:
+ return "Connection timed out";
+ case WSAECONNREFUSED:
+ return "Connection refused";
+ case WSAEHOSTDOWN:
+ return "Host is down";
+ case WSAEHOSTUNREACH:
+ return "No route to host";
+ case WSAEPROCLIM:
+ return "Too many processes";
+ case WSAEDISCON:
+ return "Graceful shutdown in progress";
+ case WSATYPE_NOT_FOUND:
+ return "Class type not found";
+ case WSAHOST_NOT_FOUND:
+ return "Host not found";
+ case WSATRY_AGAIN:
+ return "Nonauthoritative host not found";
+ case WSANO_RECOVERY:
+ return "This is a nonrecoverable error";
+ case WSANO_DATA:
+ return "Valid name, no data record of requested type";
+ case WSA_INVALID_HANDLE:
+ return "Specified event object handle is invalid";
+ case WSA_INVALID_PARAMETER:
+ return "One or more parameters are invalid";
+ case WSA_IO_INCOMPLETE:
+ return "Overlapped I/O event object not in signaled state";
+ case WSA_IO_PENDING:
+ return "Overlapped operations will complete later";
+ case WSA_NOT_ENOUGH_MEMORY:
+ return "Insufficient memory available";
+ case WSA_OPERATION_ABORTED:
+ return "Overlapped operation aborted";
+#ifdef WSAINVALIDPROCTABLE
+
+ case WSAINVALIDPROCTABLE:
+ return "Invalid procedure table from service provider";
+#endif
+#ifdef WSAINVALIDPROVIDER
+
+ case WSAINVALIDPROVIDER:
+ return "Invalid service provider version number";
+#endif
+#ifdef WSAPROVIDERFAILEDINIT
+
+ case WSAPROVIDERFAILEDINIT:
+ return "Unable to initialize a service provider";
+#endif
+
+ case WSASYSCALLFAILURE:
+ return "System call failure";
+
+ default:
+ msg = strerror (error_number);
+
+ if (msg == NULL)
+ msg = "unknown";
+
+ return msg;
+ }
+#endif //DBUS_WINCE
+}
+
+/**
+ * Assigns an error name and message corresponding to a Win32 error
+ * code to a DBusError. Does nothing if error is #NULL.
+ *
+ * @param error the error.
+ * @param code the Win32 error code
+ */
+void
+_dbus_win_set_error_from_win_error (DBusError *error,
+ int code)
+{
+ char *msg;
+
+ /* As we want the English message, use the A API */
+ FormatMessageA (FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_IGNORE_INSERTS |
+ FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL, code, MAKELANGID (LANG_ENGLISH, SUBLANG_ENGLISH_US),
+ (LPSTR) &msg, 0, NULL);
+ if (msg)
+ {
+ dbus_set_error (error, "win32.error", "%s", msg);
+ LocalFree (msg);
+ }
+ else
+ dbus_set_error (error, "win32.error", "Unknown error code %d or FormatMessage failed", code);
+}
+
+void
+_dbus_win_warn_win_error (const char *message,
+ unsigned long code)
+{
+ DBusError error;
+
+ dbus_error_init (&error);
+ _dbus_win_set_error_from_win_error (&error, code);
+ _dbus_warn ("%s: %s", message, error.message);
+ dbus_error_free (&error);
+}
+
+/**
+ * Removes a directory; Directory must be empty
+ *
+ * @param filename directory filename
+ * @param error initialized error object
+ * @returns #TRUE on success
+ */
+dbus_bool_t
+_dbus_delete_directory (const DBusString *filename,
+ DBusError *error)
+{
+ const char *filename_c;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ filename_c = _dbus_string_get_const_data (filename);
+
+ if (RemoveDirectoryA (filename_c) == 0)
+ {
+ char *emsg = _dbus_win_error_string (GetLastError ());
+ dbus_set_error (error, _dbus_win_error_from_last_error (),
+ "Failed to remove directory %s: %s",
+ filename_c, emsg);
+ _dbus_win_free_error_string (emsg);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**
+ * Checks whether the filename is an absolute path
+ *
+ * @param filename the filename
+ * @returns #TRUE if an absolute path
+ */
+dbus_bool_t
+_dbus_path_is_absolute (const DBusString *filename)
+{
+ if (_dbus_string_get_length (filename) > 0)
+ return _dbus_string_get_byte (filename, 1) == ':'
+ || _dbus_string_get_byte (filename, 0) == '\\'
+ || _dbus_string_get_byte (filename, 0) == '/';
+ else
+ return FALSE;
+}
+
+dbus_bool_t
+_dbus_check_setuid (void)
+{
+ return FALSE;
+}
+
+int
+_dbus_save_socket_errno (void)
+{
+ return errno;
+}
+
+void
+_dbus_restore_socket_errno (int saved_errno)
+{
+ _dbus_win_set_errno (saved_errno);
+}
+
+static const char *log_tag = "dbus";
+static DBusLogFlags log_flags = DBUS_LOG_FLAGS_STDERR;
+
+/**
+ * Initialize the system log.
+ *
+ * The "tag" is not copied, and must remain valid for the entire lifetime of
+ * the process or until _dbus_init_system_log() is called again. In practice
+ * it will normally be a constant.
+ *
+ * @param tag the name of the executable (syslog tag)
+ * @param mode whether to log to stderr, the system log or both
+ */
+void
+_dbus_init_system_log (const char *tag,
+ DBusLogFlags flags)
+{
+ /* We never want to turn off logging completely */
+ _dbus_assert (
+ (flags & (DBUS_LOG_FLAGS_STDERR | DBUS_LOG_FLAGS_SYSTEM_LOG)) != 0);
+
+ log_tag = tag;
+ log_flags = flags;
+}
+
+/**
+ * Log a message to the system log file (e.g. syslog on Unix).
+ *
+ * @param severity a severity value
+ * @param msg a printf-style format string
+ * @param args arguments for the format string
+ */
+void
+_dbus_logv (DBusSystemLogSeverity severity,
+ const char *msg,
+ va_list args)
+{
+ const char *s = "";
+ va_list tmp;
+
+ switch(severity)
+ {
+ case DBUS_SYSTEM_LOG_INFO: s = "info"; break;
+ case DBUS_SYSTEM_LOG_WARNING: s = "warning"; break;
+ case DBUS_SYSTEM_LOG_SECURITY: s = "security"; break;
+ case DBUS_SYSTEM_LOG_ERROR: s = "error"; break;
+ default: _dbus_assert_not_reached ("invalid log severity");
+ }
+
+ if (log_flags & DBUS_LOG_FLAGS_SYSTEM_LOG)
+ {
+ DBusString out = _DBUS_STRING_INIT_INVALID;
+ const char *message = NULL;
+ va_copy (tmp, args);
+
+ if (!_dbus_string_init (&out))
+ goto out;
+ if (!_dbus_string_append_printf (&out, "%s: ", s))
+ goto out;
+ if (!_dbus_string_append_printf_valist (&out, msg, tmp))
+ goto out;
+ message = _dbus_string_get_const_data (&out);
+out:
+ if (message != NULL)
+ {
+ OutputDebugStringA (message);
+ }
+ else
+ {
+ OutputDebugStringA ("Out of memory while formatting message: '''");
+ OutputDebugStringA (msg);
+ OutputDebugStringA ("'''");
+ }
+
+ va_end (tmp);
+ _dbus_string_free (&out);
+ }
+
+ if (log_flags & DBUS_LOG_FLAGS_STDERR)
+ {
+ va_copy (tmp, args);
+ fprintf (stderr, "%s[%lu]: %s: ", log_tag, _dbus_pid_for_log (), s);
+ vfprintf (stderr, msg, tmp);
+ fprintf (stderr, "\n");
+ va_end (tmp);
+ }
+}
+
+/*
+ * Return the low-level representation of a socket error, as used by
+ * cross-platform socket APIs like inet_ntop(), send() and recv(). This
+ * is the standard errno on Unix, but is WSAGetLastError() on Windows.
+ *
+ * Some libdbus internal functions copy this into errno, but with
+ * hindsight that was probably a design flaw.
+ */
+int
+_dbus_get_low_level_socket_errno (void)
+{
+ return WSAGetLastError ();
+}
+
+void
+_dbus_win_set_error_from_last_error (DBusError *error,
+ const char *format,
+ ...)
+{
+ const char *name;
+ char *message = NULL;
+
+ if (error == NULL)
+ return;
+
+ /* make sure to do this first, in case subsequent library calls overwrite GetLastError() */
+ name = _dbus_win_error_from_last_error ();
+ message = _dbus_win_error_string (GetLastError ());
+
+ if (format != NULL)
+ {
+ DBusString str;
+ va_list args;
+ dbus_bool_t retval;
+
+ if (!_dbus_string_init (&str))
+ {
+ _DBUS_SET_OOM (error);
+ goto out;
+ }
+
+ va_start (args, format);
+ retval = _dbus_string_append_printf_valist (&str, format, args);
+ va_end (args);
+ if (!retval)
+ {
+ _DBUS_SET_OOM (error);
+ _dbus_string_free (&str);
+ goto out;
+ }
+
+ dbus_set_error (error, name, "%s: %s", _dbus_string_get_const_data (&str), message);
+ _dbus_string_free (&str);
+ }
+ else
+ {
+ dbus_set_error (error, name, "%s", message);
+ }
+
+out:
+ if (message != NULL)
+ _dbus_win_free_error_string (message);
+
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+}
+
+/**
+ * Creates a Windows event object and returns the corresponding handle
+ *
+ * The returned object is unnamed, is a manual-reset event object,
+ * is initially in the non-signalled state, and is inheritable by child
+ * processes.
+ *
+ * @param error the error to set
+ * @return handle for the created event
+ * @return #NULL if an error has occurred, the reason is returned in \p error
+ */
+HANDLE
+_dbus_win_event_create_inheritable (DBusError *error)
+{
+ HANDLE handle;
+
+ handle = CreateEvent (NULL, TRUE, FALSE, NULL);
+ if (handle == NULL)
+ {
+ _dbus_win_set_error_from_last_error (error, "Could not create event");
+ return NULL;
+ }
+ else if (GetLastError () == ERROR_ALREADY_EXISTS)
+ {
+ _dbus_win_set_error_from_last_error (error, "Event already exists");
+ return NULL;
+ }
+
+ if (!SetHandleInformation (handle, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT))
+ {
+ _dbus_win_set_error_from_last_error (error, "Could not set inheritance for event %p", handle);
+ CloseHandle (handle);
+ return NULL;
+ }
+ return handle;
+}
+
+/**
+ * Set a Windows event to the signalled state
+ *
+ * @param handle the handle for the event to be set
+ * @return TRUE the event was set successfully
+ * @return FALSE an error has occurred, the reason is returned in \p error
+ */
+dbus_bool_t
+_dbus_win_event_set (HANDLE handle, DBusError *error)
+{
+ _dbus_assert (handle != NULL);
+
+ if (!SetEvent (handle))
+ {
+ _dbus_win_set_error_from_last_error (error, "Could not trigger event (handle %p)", handle);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/**
+ * Wait for a Windows event to enter the signalled state
+ *
+ * @param handle the handle for the event to wait for
+ * @param timeout the waiting time in milliseconds, or INFINITE to wait forever,
+ * or 0 to check immediately and not wait (polling)
+ * @param error the error to set
+ * @return TRUE the event was set successfully
+ * @return FALSE an error has occurred, the reason is returned in \p error
+ */
+dbus_bool_t
+_dbus_win_event_wait (HANDLE handle, int timeout, DBusError *error)
+{
+ DWORD status;
+
+ _dbus_assert (handle != NULL);
+
+ status = WaitForSingleObject (handle, timeout);
+ switch (status)
+ {
+ case WAIT_OBJECT_0:
+ return TRUE;
+
+ case WAIT_FAILED:
+ {
+ _dbus_win_set_error_from_last_error (error, "Unable to wait for event (handle %p)", handle);
+ return FALSE;
+ }
+
+ case WAIT_TIMEOUT:
+ /* GetLastError() is not set */
+ dbus_set_error (error, DBUS_ERROR_TIMEOUT, "Timed out waiting for event (handle %p)", handle);
+ return FALSE;
+
+ default:
+ /* GetLastError() is probably not set? */
+ dbus_set_error (error, DBUS_ERROR_FAILED, "Unknown result '%lu' while waiting for event (handle %p)", status, handle);
+ return FALSE;
+ }
+}
+
+/**
+ * Delete a Windows event
+ *
+ * @param handle handle for the event to delete
+ * @param error the error to set (optional)
+ * @return TRUE the event has been deleted successfully or the handle is one of the special sentinel values #NULL or #INVALID_HANDLE_VALUE
+ * @return FALSE an error has occurred, the reason is returned in \p error if specified
+ */
+dbus_bool_t
+_dbus_win_event_free (HANDLE handle, DBusError *error)
+{
+ if (handle == NULL || handle == INVALID_HANDLE_VALUE)
+ return TRUE;
+
+ if (CloseHandle (handle))
+ return TRUE;
+
+ /* the handle may already be closed */
+ if (GetLastError () == ERROR_INVALID_HANDLE)
+ return TRUE;
+
+ _dbus_win_set_error_from_last_error (error, "Could not close event (handle %p)", handle);
+ return FALSE;
+}
+
+#ifdef HAVE_AFUNIX_H
+static dbus_bool_t
+_dbus_open_socket (SOCKET *socket_p,
+ int domain,
+ int type,
+ int protocol,
+ DBusError *error)
+{
+ if (!_dbus_win_startup_winsock ())
+ {
+ _DBUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ *socket_p = socket (domain, type, protocol);
+ if (*socket_p == INVALID_SOCKET)
+ {
+ DBUS_SOCKET_SET_ERRNO ();
+ dbus_set_error (error, _dbus_error_from_errno (errno),
+ "Failed to open socket: %s",
+ _dbus_strerror_from_errno ());
+ return FALSE;
+ }
+
+ _dbus_win_handle_set_close_on_exec ((HANDLE) *socket_p);
+ return TRUE;
+}
+
+/**
+ * Opens a UNIX domain socket (as in the socket() call).
+ * Does not bind the socket.
+ *
+ * This will set CLOEXEC for the socket returned
+ *
+ * @param return location for socket descriptor
+ * @param error return location for an error
+ * @returns #FALSE if error is set
+ */
+static dbus_bool_t
+_dbus_open_unix_socket (SOCKET *socket,
+ DBusError *error)
+{
+ return _dbus_open_socket (socket, AF_UNIX, SOCK_STREAM, 0, error);
+}
+#endif /* HAVE_AFUNIX_H */
+
+/**
+ * Creates a socket and connects it to the UNIX domain socket at the
+ * given path. The socket is returned, and is set up as
+ * nonblocking.
+ *
+ * Abstract socket usage always fails.
+ *
+ * This will set FD_CLOEXEC for the socket returned.
+ *
+ * @param path the path to UNIX domain socket
+ * @param abstract #TRUE to use abstract namespace
+ * @param error return location for error code
+ * @returns a valid socket on success or an invalid socket on error
+ */
+DBusSocket
+_dbus_connect_unix_socket (const char *path,
+ dbus_bool_t abstract,
+ DBusError *error)
+{
+ DBusSocket s = DBUS_SOCKET_INIT;
+
+#ifdef HAVE_AFUNIX_H
+ struct sockaddr_un addr;
+ size_t path_len;
+
+ _DBUS_STATIC_ASSERT (sizeof (addr.sun_path) > _DBUS_MAX_SUN_PATH_LENGTH);
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ _dbus_verbose ("connecting to unix socket %s abstract=%d\n",
+ path, abstract);
+
+ if (abstract)
+ {
+ dbus_set_error (error, DBUS_ERROR_NOT_SUPPORTED,
+ "Failed to connect: UNIX abstract socket is not supported on this system");
+ return s;
+ }
+
+ path_len = strlen (path);
+ if (path_len > _DBUS_MAX_SUN_PATH_LENGTH)
+ {
+ dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS,
+ "Failed to connect: socket name too long");
+ return s;
+ }
+
+ if (!_dbus_open_unix_socket (&s.sock, error))
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ return s;
+ }
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ _DBUS_ZERO (addr);
+ addr.sun_family = AF_UNIX;
+ strncpy (addr.sun_path, path, sizeof (addr.sun_path) - 1);
+
+ if (connect (s.sock, (struct sockaddr *) &addr, _DBUS_STRUCT_OFFSET (struct sockaddr_un, sun_path) + path_len) < 0)
+ {
+ DBUS_SOCKET_SET_ERRNO ();
+ dbus_set_error (error,
+ _dbus_error_from_errno (errno),
+ "Failed to connect to socket %s: %s",
+ path, _dbus_strerror (errno));
+
+ _dbus_close_socket (&s, NULL);
+ return s;
+ }
+
+ if (!_dbus_set_socket_nonblocking (s, error))
+ _dbus_close_socket (&s, NULL);
+
+#else
+ dbus_set_error (error, DBUS_ERROR_NOT_SUPPORTED,
+ "Failed to connect: UNIX socket is not supported with this build");
+#endif
+
+ return s;
+}
+
+/**
+ * Creates a socket and binds it to the given path,
+ * then listens on the socket. The socket is
+ * set to be nonblocking.
+ *
+ * Abstract socket usage always fails.
+ *
+ * This will set CLOEXEC for the socket returned
+ *
+ * @param path the socket name
+ * @param abstract #TRUE to use abstract namespace
+ * @param error return location for errors
+ * @returns a valid socket on success or an invalid socket on error
+ */
+DBusSocket
+_dbus_listen_unix_socket (const char *path,
+ dbus_bool_t abstract,
+ DBusError *error)
+{
+ DBusSocket s = DBUS_SOCKET_INIT;
+
+#ifdef HAVE_AFUNIX_H
+ struct sockaddr_un addr;
+ size_t path_len;
+ _DBUS_STATIC_ASSERT (sizeof (addr.sun_path) > _DBUS_MAX_SUN_PATH_LENGTH);
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ _dbus_verbose ("listening on unix socket %s abstract=%d\n",
+ path, abstract);
+
+ if (abstract)
+ {
+ dbus_set_error (error, DBUS_ERROR_NOT_SUPPORTED,
+ "Failed to listen: UNIX abstract socket is not supported on this system");
+ return s;
+ }
+
+ if (!_dbus_open_unix_socket (&s.sock, error))
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ return s;
+ }
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ _DBUS_ZERO (addr);
+ addr.sun_family = AF_UNIX;
+ path_len = strlen (path);
+
+ /* see related comment in dbus-sysdeps-unix.c */
+ /* there is no S_ISSOCK on windows yet, so just unlink the path */
+ unlink (path);
+
+ if (path_len > _DBUS_MAX_SUN_PATH_LENGTH)
+ {
+ dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS,
+ "Failed to listen: socket name too long");
+ _dbus_close_socket (&s, NULL);
+ return s;
+ }
+
+ strncpy (addr.sun_path, path, sizeof (addr.sun_path) - 1);
+
+ if (bind (s.sock, (struct sockaddr *) &addr, _DBUS_STRUCT_OFFSET (struct sockaddr_un, sun_path) + path_len) < 0)
+ {
+ DBUS_SOCKET_SET_ERRNO ();
+ dbus_set_error (error, _dbus_error_from_errno (errno),
+ "Failed to bind socket \"%s\": %s",
+ path, _dbus_strerror (errno));
+ _dbus_close_socket (&s, NULL);
+ return s;
+ }
+
+ if (listen (s.sock, SOMAXCONN /* backlog */) < 0)
+ {
+ DBUS_SOCKET_SET_ERRNO ();
+ dbus_set_error (error, _dbus_error_from_errno (errno),
+ "Failed to listen on socket \"%s\": %s",
+ path, _dbus_strerror (errno));
+ _dbus_close_socket (&s, NULL);
+ return s;
+ }
+
+ if (!_dbus_set_socket_nonblocking (s, error))
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ _dbus_close_socket (&s, NULL);
+ return s;
+ }
+#else
+ dbus_set_error (error, DBUS_ERROR_NOT_SUPPORTED,
+ "Failed to listen: UNIX socket is not supported with this build");
+#endif
+
+ return s;
+}
+
+/** @} end of sysdeps-win */
+/* tests in dbus-sysdeps-util.c */
diff --git a/src/3rdparty/libdbus/dbus/dbus-sysdeps-win.h b/src/3rdparty/libdbus/dbus/dbus-sysdeps-win.h
new file mode 100644
index 00000000..f7be2101
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-sysdeps-win.h
@@ -0,0 +1,125 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-sysdeps.c Wrappers around system/libc features (internal to D-BUS implementation)
+ *
+ * Copyright (C) 2002, 2003 Red Hat, Inc.
+ * Copyright (C) 2003 CodeFactory AB
+ * Copyright (C) 2005 Novell, Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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
+ *
+ */
+
+#ifndef DBUS_SYSDEPS_WIN_H
+#define DBUS_SYSDEPS_WIN_H
+
+extern void *_dbus_win_get_dll_hmodule (void);
+#define WIN32_LEAN_AND_MEAN
+
+#include "dbus-hash.h"
+#include "dbus-string.h"
+#include "dbus-threads-internal.h"
+#include <ctype.h>
+#include <malloc.h>
+#include <windows.h>
+#undef interface
+
+#define DBUS_CONSOLE_DIR "/var/run/console/"
+
+
+void _dbus_win_set_errno (int err);
+DBUS_PRIVATE_EXPORT
+const char* _dbus_win_error_from_last_error (void);
+
+dbus_bool_t _dbus_win_startup_winsock (void);
+void _dbus_win_warn_win_error (const char *message,
+ unsigned long code);
+DBUS_PRIVATE_EXPORT
+char * _dbus_win_error_string (int error_number);
+DBUS_PRIVATE_EXPORT
+void _dbus_win_free_error_string (char *string);
+
+extern const char* _dbus_lm_strerror (int error_number);
+
+
+dbus_bool_t _dbus_win_account_to_sid (const wchar_t *waccount,
+ void **ppsid,
+ DBusError *error);
+
+dbus_bool_t
+_dbus_win32_sid_to_name_and_domain (dbus_uid_t uid,
+ wchar_t **wname,
+ wchar_t **wdomain,
+ DBusError *error);
+
+
+/* Don't define DBUS_CONSOLE_DIR on Win32 */
+
+wchar_t *_dbus_win_utf8_to_utf16 (const char *str,
+ DBusError *error);
+char *_dbus_win_utf16_to_utf8 (const wchar_t *str,
+ DBusError *error);
+
+DBUS_PRIVATE_EXPORT
+void _dbus_win_set_error_from_win_error (DBusError *error, int code);
+
+dbus_bool_t
+_dbus_win_sid_to_name_and_domain (dbus_uid_t uid,
+ wchar_t **wname,
+ wchar_t **wdomain,
+ DBusError *error);
+
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_get_install_root (DBusString *str);
+
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_getsid(char **sid, dbus_pid_t process_id);
+
+HANDLE _dbus_spawn_program (const char *name,
+ char **argv,
+ char **envp,
+ dbus_bool_t inherit_handles,
+ DBusError *error);
+
+DBUS_PRIVATE_EXPORT
+void _dbus_win_set_error_from_last_error (DBusError *error,
+ const char *format,
+ ...) _DBUS_GNUC_PRINTF (2, 3);
+
+DBUS_PRIVATE_EXPORT
+HANDLE _dbus_win_event_create_inheritable (DBusError *error);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_win_event_set (HANDLE handle, DBusError *error);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_win_event_wait (HANDLE handle, int timeout, DBusError *error);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_win_event_free (HANDLE handle, DBusError *error);
+
+dbus_bool_t _dbus_daemon_is_session_bus_address_published (const char *scope);
+dbus_bool_t _dbus_daemon_publish_session_bus_address (const char *address,
+ const char *shm_name);
+DBUS_PRIVATE_EXPORT
+DBusRMutex *_dbus_win_rmutex_named_new (const char* name);
+
+DBUS_PRIVATE_EXPORT
+void _dbus_test_win_autolaunch_set_command_line_parameter (const char *path);
+DBUS_PRIVATE_EXPORT
+void _dbus_test_win_set_autolaunch_handle_location (HANDLE *location);
+#endif
+
+/** @} end of sysdeps-win.h */
diff --git a/src/3rdparty/libdbus/dbus/dbus-sysdeps.c b/src/3rdparty/libdbus/dbus/dbus-sysdeps.c
new file mode 100644
index 00000000..8b82bfd5
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-sysdeps.c
@@ -0,0 +1,1021 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-sysdeps.c Wrappers around system/libc features shared between UNIX and Windows (internal to D-Bus implementation)
+ *
+ * Copyright (C) 2002, 2003, 2006 Red Hat, Inc.
+ * Copyright (C) 2003 CodeFactory AB
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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-sysdeps.h"
+#include "dbus-threads.h"
+#include "dbus-protocol.h"
+#include "dbus-string.h"
+#include "dbus-list.h"
+#include "dbus-misc.h"
+
+/* NOTE: If you include any unix/windows-specific headers here, you are probably doing something
+ * wrong and should be putting some code in dbus-sysdeps-unix.c or dbus-sysdeps-win.c.
+ *
+ * These are the standard ANSI C headers...
+ */
+#if HAVE_LOCALE_H
+#include <locale.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#ifdef DBUS_WIN
+ #include <stdlib.h>
+#elif (defined __APPLE__)
+# include <crt_externs.h>
+# define environ (*_NSGetEnviron())
+#elif HAVE_DECL_ENVIRON && defined(HAVE_UNISTD_H)
+# include <unistd.h>
+#else
+extern char **environ;
+#endif
+
+#ifdef DBUS_WIN
+#include "dbus-sockets-win.h"
+#else
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#endif
+
+/**
+ * @defgroup DBusSysdeps Internal system-dependent API
+ * @ingroup DBusInternals
+ * @brief Internal system-dependent API available on UNIX and Windows
+ *
+ * The system-dependent API has a dual purpose. First, it encapsulates
+ * all usage of operating system APIs for ease of auditing and to
+ * avoid cluttering the rest of the code with bizarre OS quirks and
+ * headers. Second, it abstracts different operating system APIs for
+ * portability.
+ *
+ * @{
+ */
+
+/**
+ * Aborts the program with SIGABRT (dumping core).
+ */
+void
+_dbus_abort (void)
+{
+ const char *s;
+
+ _dbus_print_backtrace ();
+
+ s = _dbus_getenv ("DBUS_BLOCK_ON_ABORT");
+ if (s && *s)
+ {
+ /* don't use _dbus_warn here since it can _dbus_abort() */
+ fprintf (stderr, " Process %lu sleeping for gdb attach\n", _dbus_pid_for_log ());
+ _dbus_sleep_milliseconds (1000 * 180);
+ }
+
+ abort ();
+ _dbus_exit (1); /* in case someone manages to ignore SIGABRT ? */
+}
+
+/**
+ * @ingroup DBusMisc
+ *
+ * Wrapper for setenv(). If the value is #NULL, unsets
+ * the environment variable.
+ *
+ * There is an unfixable memleak in that it is unsafe to
+ * free memory malloced for use with setenv. This is because
+ * we can not rely on internal implementation details of
+ * the underlying libc library.
+ *
+ * This function is not thread-safe, because altering the environment
+ * in Unix is not thread-safe in general.
+ *
+ * @param varname name of environment variable
+ * @param value value of environment variable, or #NULL to unset
+ * @returns #TRUE on success, #FALSE if not enough memory.
+ */
+dbus_bool_t
+dbus_setenv (const char *varname,
+ const char *value)
+{
+ _dbus_assert (varname != NULL);
+
+ if (value == NULL)
+ {
+#ifdef HAVE_UNSETENV
+ unsetenv (varname);
+ return TRUE;
+#else
+ char *putenv_value;
+ size_t len;
+
+ len = strlen (varname);
+
+ /* Use system malloc to avoid memleaks that dbus_malloc
+ * will get upset about.
+ */
+
+ putenv_value = malloc (len + 2);
+ if (putenv_value == NULL)
+ return FALSE;
+
+ strcpy (putenv_value, varname);
+#if defined(DBUS_WIN)
+ strcat (putenv_value, "=");
+#endif
+
+ return (putenv (putenv_value) == 0);
+#endif
+ }
+ else
+ {
+#ifdef HAVE_SETENV
+ return (setenv (varname, value, TRUE) == 0);
+#else
+ char *putenv_value;
+ size_t len;
+ size_t varname_len;
+ size_t value_len;
+
+ varname_len = strlen (varname);
+ value_len = strlen (value);
+
+ len = varname_len + value_len + 1 /* '=' */ ;
+
+ /* Use system malloc to avoid memleaks that dbus_malloc
+ * will get upset about.
+ */
+
+ putenv_value = malloc (len + 1);
+ if (putenv_value == NULL)
+ return FALSE;
+
+ strcpy (putenv_value, varname);
+ strcpy (putenv_value + varname_len, "=");
+ strcpy (putenv_value + varname_len + 1, value);
+
+ return (putenv (putenv_value) == 0);
+#endif
+ }
+}
+
+/**
+ * Wrapper for getenv().
+ *
+ * @param varname name of environment variable
+ * @returns value of environment variable or #NULL if unset
+ */
+const char*
+_dbus_getenv (const char *varname)
+{
+ /* Don't respect any environment variables if the current process is
+ * setuid. This is the equivalent of glibc's __secure_getenv().
+ */
+ if (_dbus_check_setuid ())
+ return NULL;
+ return getenv (varname);
+}
+
+/**
+ * Wrapper for clearenv().
+ *
+ * @returns #TRUE on success.
+ */
+dbus_bool_t
+_dbus_clearenv (void)
+{
+ dbus_bool_t rc = TRUE;
+
+#ifdef HAVE_CLEARENV
+ if (clearenv () != 0)
+ rc = FALSE;
+#else
+
+ if (environ != NULL)
+ environ[0] = NULL;
+#endif
+
+ return rc;
+}
+
+/**
+ * Split paths into a list of char strings
+ *
+ * @param dirs string with pathes
+ * @param suffix string concated to each path in dirs
+ * @param dir_list contains a list of splitted pathes
+ * return #TRUE is pathes could be splittes,#FALSE in oom case
+ */
+dbus_bool_t
+_dbus_split_paths_and_append (DBusString *dirs,
+ const char *suffix,
+ DBusList **dir_list)
+{
+ int start;
+ int i;
+ int len;
+ char *cpath;
+ DBusString file_suffix;
+
+ start = 0;
+ i = 0;
+
+ _dbus_string_init_const (&file_suffix, suffix);
+
+ len = _dbus_string_get_length (dirs);
+
+ while (_dbus_string_find (dirs, start, _DBUS_PATH_SEPARATOR, &i))
+ {
+ DBusString path;
+
+ if (!_dbus_string_init (&path))
+ goto oom;
+
+ if (!_dbus_string_copy_len (dirs,
+ start,
+ i - start,
+ &path,
+ 0))
+ {
+ _dbus_string_free (&path);
+ goto oom;
+ }
+
+ _dbus_string_chop_white (&path);
+
+ /* check for an empty path */
+ if (_dbus_string_get_length (&path) == 0)
+ goto next;
+
+ if (!_dbus_concat_dir_and_file (&path,
+ &file_suffix))
+ {
+ _dbus_string_free (&path);
+ goto oom;
+ }
+
+ if (!_dbus_string_copy_data(&path, &cpath))
+ {
+ _dbus_string_free (&path);
+ goto oom;
+ }
+
+ if (!_dbus_list_append (dir_list, cpath))
+ {
+ _dbus_string_free (&path);
+ dbus_free (cpath);
+ goto oom;
+ }
+
+ next:
+ _dbus_string_free (&path);
+ start = i + 1;
+ }
+
+ if (start != len)
+ {
+ DBusString path;
+
+ if (!_dbus_string_init (&path))
+ goto oom;
+
+ if (!_dbus_string_copy_len (dirs,
+ start,
+ len - start,
+ &path,
+ 0))
+ {
+ _dbus_string_free (&path);
+ goto oom;
+ }
+
+ if (!_dbus_concat_dir_and_file (&path,
+ &file_suffix))
+ {
+ _dbus_string_free (&path);
+ goto oom;
+ }
+
+ if (!_dbus_string_copy_data(&path, &cpath))
+ {
+ _dbus_string_free (&path);
+ goto oom;
+ }
+
+ if (!_dbus_list_append (dir_list, cpath))
+ {
+ _dbus_string_free (&path);
+ dbus_free (cpath);
+ goto oom;
+ }
+
+ _dbus_string_free (&path);
+ }
+
+ return TRUE;
+
+ oom:
+ _dbus_list_clear_full (dir_list, dbus_free);
+ return FALSE;
+}
+
+/** @} */
+
+/**
+ * @addtogroup DBusString
+ *
+ * @{
+ */
+/**
+ * Appends an integer to a DBusString.
+ *
+ * @param str the string
+ * @param value the integer value
+ * @returns #FALSE if not enough memory or other failure.
+ */
+dbus_bool_t
+_dbus_string_append_int (DBusString *str,
+ long value)
+{
+ /* this calculation is from comp.lang.c faq */
+#define MAX_LONG_LEN ((sizeof (long) * 8 + 2) / 3 + 1) /* +1 for '-' */
+ int orig_len;
+ int i;
+ char *buf;
+
+ orig_len = _dbus_string_get_length (str);
+
+ if (!_dbus_string_lengthen (str, MAX_LONG_LEN))
+ return FALSE;
+
+ buf = _dbus_string_get_data_len (str, orig_len, MAX_LONG_LEN);
+
+ snprintf (buf, MAX_LONG_LEN, "%ld", value);
+
+ i = 0;
+ while (*buf)
+ {
+ ++buf;
+ ++i;
+ }
+
+ _dbus_string_shorten (str, MAX_LONG_LEN - i);
+
+ return TRUE;
+}
+
+/**
+ * Appends an unsigned integer to a DBusString.
+ *
+ * @param str the string
+ * @param value the integer value
+ * @returns #FALSE if not enough memory or other failure.
+ */
+dbus_bool_t
+_dbus_string_append_uint (DBusString *str,
+ unsigned long value)
+{
+ /* this is wrong, but definitely on the high side. */
+#define MAX_ULONG_LEN (MAX_LONG_LEN * 2)
+ int orig_len;
+ int i;
+ char *buf;
+
+ orig_len = _dbus_string_get_length (str);
+
+ if (!_dbus_string_lengthen (str, MAX_ULONG_LEN))
+ return FALSE;
+
+ buf = _dbus_string_get_data_len (str, orig_len, MAX_ULONG_LEN);
+
+ snprintf (buf, MAX_ULONG_LEN, "%lu", value);
+
+ i = 0;
+ while (*buf)
+ {
+ ++buf;
+ ++i;
+ }
+
+ _dbus_string_shorten (str, MAX_ULONG_LEN - i);
+
+ return TRUE;
+}
+
+/**
+ * Parses an integer contained in a DBusString. Either return parameter
+ * may be #NULL if you aren't interested in it. The integer is parsed
+ * and stored in value_return. Return parameters are not initialized
+ * if the function returns #FALSE.
+ *
+ * @param str the string
+ * @param start the byte index of the start of the integer
+ * @param value_return return location of the integer value or #NULL
+ * @param end_return return location of the end of the integer, or #NULL
+ * @returns #TRUE on success
+ */
+dbus_bool_t
+_dbus_string_parse_int (const DBusString *str,
+ int start,
+ long *value_return,
+ int *end_return)
+{
+ long v;
+ const char *p;
+ char *end;
+
+ p = _dbus_string_get_const_data_len (str, start,
+ _dbus_string_get_length (str) - start);
+
+ end = NULL;
+ _dbus_set_errno_to_zero ();
+ v = strtol (p, &end, 0);
+ if (end == NULL || end == p || errno != 0)
+ return FALSE;
+
+ if (value_return)
+ *value_return = v;
+ if (end_return)
+ *end_return = start + (end - p);
+
+ return TRUE;
+}
+
+/**
+ * Parses an unsigned integer contained in a DBusString. Either return
+ * parameter may be #NULL if you aren't interested in it. The integer
+ * is parsed and stored in value_return. Return parameters are not
+ * initialized if the function returns #FALSE.
+ *
+ * @param str the string
+ * @param start the byte index of the start of the integer
+ * @param value_return return location of the integer value or #NULL
+ * @param end_return return location of the end of the integer, or #NULL
+ * @returns #TRUE on success
+ */
+dbus_bool_t
+_dbus_string_parse_uint (const DBusString *str,
+ int start,
+ unsigned long *value_return,
+ int *end_return)
+{
+ unsigned long v;
+ const char *p;
+ char *end;
+
+ p = _dbus_string_get_const_data_len (str, start,
+ _dbus_string_get_length (str) - start);
+
+ end = NULL;
+ _dbus_set_errno_to_zero ();
+ v = strtoul (p, &end, 0);
+ if (end == NULL || end == p || errno != 0)
+ return FALSE;
+
+ if (value_return)
+ *value_return = v;
+ if (end_return)
+ *end_return = start + (end - p);
+
+ return TRUE;
+}
+
+/**
+ * Parses a dbus_int64_t integer contained in a DBusString. Either return parameter
+ * may be #NULL if you aren't interested in it. The integer is parsed
+ * and stored in value_return. Return parameters are not initialized
+ * if the function returns #FALSE.
+ *
+ * @param str the string
+ * @param start the byte index of the start of the integer
+ * @param value_return return location of the integer value or #NULL
+ * @param end_return return location of the end of the integer, or #NULL
+ * @returns #TRUE on success
+ */
+dbus_bool_t
+_dbus_string_parse_int64 (const DBusString *str,
+ int start,
+ dbus_int64_t *value_return,
+ int *end_return)
+{
+ dbus_int64_t v;
+ const char *p;
+ char *end;
+
+ p = _dbus_string_get_const_data_len (str, start,
+ _dbus_string_get_length (str) - start);
+
+ end = NULL;
+ _dbus_set_errno_to_zero ();
+ v = strtoll (p, &end, 0);
+ if (end == NULL || end == p || errno != 0)
+ return FALSE;
+
+ if (value_return)
+ *value_return = v;
+ if (end_return)
+ *end_return = start + (end - p);
+
+ return TRUE;
+}
+
+/** @} */ /* DBusString group */
+
+/**
+ * @addtogroup DBusInternalsUtils
+ * @{
+ */
+
+/**
+ * Fills n_bytes of the given buffer with random bytes.
+ *
+ * @param buffer an allocated buffer
+ * @param n_bytes the number of bytes in buffer to write to
+ * @param error location to store reason for failure
+ * @returns #TRUE on success
+ */
+dbus_bool_t
+_dbus_generate_random_bytes_buffer (char *buffer,
+ int n_bytes,
+ DBusError *error)
+{
+ DBusString str;
+
+ if (!_dbus_string_init (&str))
+ {
+ _DBUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ if (!_dbus_generate_random_bytes (&str, n_bytes, error))
+ {
+ _dbus_string_free (&str);
+ return FALSE;
+ }
+
+ _dbus_string_copy_to_buffer (&str, buffer, n_bytes);
+
+ _dbus_string_free (&str);
+ return TRUE;
+}
+
+/**
+ * Generates the given number of random bytes, where the bytes are
+ * chosen from the alphanumeric ASCII subset.
+ *
+ * @param str the string
+ * @param n_bytes the number of random ASCII bytes to append to string
+ * @param error location to store reason for failure
+ * @returns #TRUE on success, #FALSE if no memory or other failure
+ */
+dbus_bool_t
+_dbus_generate_random_ascii (DBusString *str,
+ int n_bytes,
+ DBusError *error)
+{
+ static const char letters[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz";
+ int i;
+ int len;
+
+ if (!_dbus_generate_random_bytes (str, n_bytes, error))
+ return FALSE;
+
+ len = _dbus_string_get_length (str);
+ i = len - n_bytes;
+ while (i < len)
+ {
+ _dbus_string_set_byte (str, i,
+ letters[_dbus_string_get_byte (str, i) %
+ (sizeof (letters) - 1)]);
+
+ ++i;
+ }
+
+ _dbus_assert (_dbus_string_validate_ascii (str, len - n_bytes,
+ n_bytes));
+
+ return TRUE;
+}
+
+/**
+ * Converts a UNIX errno, or Windows errno or WinSock error value into
+ * a #DBusError name.
+ *
+ * @todo should cover more errnos, specifically those
+ * from open().
+ *
+ * @param error_number the errno.
+ * @returns an error name
+ */
+const char*
+_dbus_error_from_errno (int error_number)
+{
+ switch (error_number)
+ {
+ case 0:
+ return DBUS_ERROR_FAILED;
+
+#ifdef EPROTONOSUPPORT
+ case EPROTONOSUPPORT:
+ return DBUS_ERROR_NOT_SUPPORTED;
+#elif defined(WSAEPROTONOSUPPORT)
+ case WSAEPROTONOSUPPORT:
+ return DBUS_ERROR_NOT_SUPPORTED;
+#endif
+#ifdef EAFNOSUPPORT
+ case EAFNOSUPPORT:
+ return DBUS_ERROR_NOT_SUPPORTED;
+#elif defined(WSAEAFNOSUPPORT)
+ case WSAEAFNOSUPPORT:
+ return DBUS_ERROR_NOT_SUPPORTED;
+#endif
+#ifdef ENFILE
+ case ENFILE:
+ return DBUS_ERROR_LIMITS_EXCEEDED; /* kernel out of memory */
+#endif
+#ifdef EMFILE
+ case EMFILE:
+ return DBUS_ERROR_LIMITS_EXCEEDED;
+#endif
+#ifdef EACCES
+ case EACCES:
+ return DBUS_ERROR_ACCESS_DENIED;
+#endif
+#ifdef EPERM
+ case EPERM:
+ return DBUS_ERROR_ACCESS_DENIED;
+#endif
+#ifdef ENOBUFS
+ case ENOBUFS:
+ return DBUS_ERROR_NO_MEMORY;
+#endif
+#ifdef ENOMEM
+ case ENOMEM:
+ return DBUS_ERROR_NO_MEMORY;
+#endif
+#ifdef ECONNREFUSED
+ case ECONNREFUSED:
+ return DBUS_ERROR_NO_SERVER;
+#elif defined(WSAECONNREFUSED)
+ case WSAECONNREFUSED:
+ return DBUS_ERROR_NO_SERVER;
+#endif
+#ifdef ETIMEDOUT
+ case ETIMEDOUT:
+ return DBUS_ERROR_TIMEOUT;
+#elif defined(WSAETIMEDOUT)
+ case WSAETIMEDOUT:
+ return DBUS_ERROR_TIMEOUT;
+#endif
+#ifdef ENETUNREACH
+ case ENETUNREACH:
+ return DBUS_ERROR_NO_NETWORK;
+#elif defined(WSAENETUNREACH)
+ case WSAENETUNREACH:
+ return DBUS_ERROR_NO_NETWORK;
+#endif
+#ifdef EADDRINUSE
+ case EADDRINUSE:
+ return DBUS_ERROR_ADDRESS_IN_USE;
+#elif defined(WSAEADDRINUSE)
+ case WSAEADDRINUSE:
+ return DBUS_ERROR_ADDRESS_IN_USE;
+#endif
+#ifdef EEXIST
+ case EEXIST:
+ return DBUS_ERROR_FILE_EXISTS;
+#endif
+#ifdef ENOENT
+ case ENOENT:
+ return DBUS_ERROR_FILE_NOT_FOUND;
+#endif
+ default:
+ return DBUS_ERROR_FAILED;
+ }
+}
+
+/**
+ * Converts the current system errno value into a #DBusError name.
+ *
+ * @returns an error name
+ */
+const char*
+_dbus_error_from_system_errno (void)
+{
+ return _dbus_error_from_errno (errno);
+}
+
+/**
+ * Assign 0 to the global errno variable
+ */
+void
+_dbus_set_errno_to_zero (void)
+{
+#ifdef DBUS_WINCE
+ SetLastError (0);
+#else
+ errno = 0;
+#endif
+}
+
+/**
+ * See if errno is ENOMEM
+ * @returns #TRUE if e == ENOMEM
+ */
+dbus_bool_t
+_dbus_get_is_errno_enomem (int e)
+{
+ return e == ENOMEM;
+}
+
+/**
+ * See if errno is EINTR
+ * @returns #TRUE if e == EINTR
+ */
+dbus_bool_t
+_dbus_get_is_errno_eintr (int e)
+{
+ return e == EINTR;
+}
+
+/**
+ * See if errno is EPIPE
+ * @returns #TRUE if errno == EPIPE
+ */
+dbus_bool_t
+_dbus_get_is_errno_epipe (int e)
+{
+ return e == EPIPE;
+}
+
+/**
+ * See if errno is ETOOMANYREFS
+ * @returns #TRUE if errno == ETOOMANYREFS
+ */
+dbus_bool_t
+_dbus_get_is_errno_etoomanyrefs (int e)
+{
+#ifdef ETOOMANYREFS
+ return e == ETOOMANYREFS;
+#else
+ return FALSE;
+#endif
+}
+
+/**
+ * Get error message from errno
+ * @returns _dbus_strerror(errno)
+ */
+const char*
+_dbus_strerror_from_errno (void)
+{
+ return _dbus_strerror (errno);
+}
+
+/**
+ * Log a message to the system log file (e.g. syslog on Unix) and/or stderr.
+ *
+ * @param severity a severity value
+ * @param msg a printf-style format string
+ */
+void
+_dbus_log (DBusSystemLogSeverity severity,
+ const char *msg,
+ ...)
+{
+ va_list args;
+
+ va_start (args, msg);
+
+ _dbus_logv (severity, msg, args);
+
+ va_end (args);
+}
+
+/*
+ * Try to convert the IPv4 or IPv6 address pointed to by
+ * sockaddr_pointer into a string.
+ *
+ * @param sockaddr_pointer A struct sockaddr_in or struct sockaddr_in6
+ * @param len The length of the struct pointed to by sockaddr_pointer
+ * @param string An array to write the address into
+ * @param string_len Length of string (should usually be at least INET6_ADDRSTRLEN)
+ * @param family_name Used to return "ipv4" or "ipv6", or NULL to ignore
+ * @param port Used to return the port number, or NULL to ignore
+ * @returns #FALSE with errno set if the address family was not understood
+ */
+dbus_bool_t
+_dbus_inet_sockaddr_to_string (const void *sockaddr_pointer,
+ size_t len,
+ char *string,
+ size_t string_len,
+ const char **family_name,
+ dbus_uint16_t *port,
+ DBusError *error)
+{
+ union
+ {
+ struct sockaddr sa;
+ struct sockaddr_storage storage;
+ struct sockaddr_in ipv4;
+ struct sockaddr_in6 ipv6;
+ } addr;
+ int saved_errno;
+
+ if (len > sizeof (addr))
+ return FALSE;
+
+ _DBUS_ZERO (addr);
+ memcpy (&addr, sockaddr_pointer, len);
+
+ switch (addr.sa.sa_family)
+ {
+ case AF_INET:
+ if (inet_ntop (AF_INET, &addr.ipv4.sin_addr, string, string_len) != NULL)
+ {
+ if (family_name != NULL)
+ *family_name = "ipv4";
+
+ if (port != NULL)
+ *port = ntohs (addr.ipv4.sin_port);
+
+ return TRUE;
+ }
+ else
+ {
+ saved_errno = _dbus_get_low_level_socket_errno ();
+ dbus_set_error (error, _dbus_error_from_errno (saved_errno),
+ "Failed to get identity of IPv4 socket: %s",
+ _dbus_strerror (saved_errno));
+ }
+
+ return FALSE;
+
+#ifdef AF_INET6
+ case AF_INET6:
+ if (inet_ntop (AF_INET6, &addr.ipv6.sin6_addr, string, string_len) != NULL)
+ {
+ if (family_name != NULL)
+ *family_name = "ipv6";
+
+ if (port != NULL)
+ *port = ntohs (addr.ipv6.sin6_port);
+
+ return TRUE;
+ }
+ else
+ {
+ saved_errno = _dbus_get_low_level_socket_errno ();
+ dbus_set_error (error, _dbus_error_from_errno (saved_errno),
+ "Failed to get identity of IPv6 socket: %s",
+ _dbus_strerror (saved_errno));
+ }
+
+ return FALSE;
+#endif
+
+ default:
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "Failed to get identity of socket: unknown family");
+ return FALSE;
+ }
+}
+
+/*
+ * Format an error appropriate for saved_errno for the IPv4 or IPv6
+ * address pointed to by sockaddr_pointer of length sockaddr_len.
+ *
+ * @param error The error to set
+ * @param sockaddr_pointer A struct sockaddr_in or struct sockaddr_in6
+ * @param len The length of the struct pointed to by sockaddr_pointer
+ * @param description A prefix like "Failed to listen on socket"
+ * @param saved_errno The OS-level error number to use
+ */
+void
+_dbus_set_error_with_inet_sockaddr (DBusError *error,
+ const void *sockaddr_pointer,
+ size_t len,
+ const char *description,
+ int saved_errno)
+{
+ char string[INET6_ADDRSTRLEN];
+ dbus_uint16_t port;
+ const struct sockaddr *addr = sockaddr_pointer;
+
+ if (_dbus_inet_sockaddr_to_string (sockaddr_pointer, len,
+ string, sizeof (string), NULL, &port,
+ NULL))
+ {
+ dbus_set_error (error, _dbus_error_from_errno (saved_errno),
+ "%s \"%s\" port %u: %s",
+ description, string, port, _dbus_strerror (saved_errno));
+ }
+ else
+ {
+ dbus_set_error (error, _dbus_error_from_errno (saved_errno),
+ "%s <address of unknown family %d>: %s",
+ description, addr->sa_family,
+ _dbus_strerror (saved_errno));
+ }
+}
+
+void
+_dbus_combine_tcp_errors (DBusList **sources,
+ const char *summary,
+ const char *host,
+ const char *port,
+ DBusError *dest)
+{
+ DBusString message = _DBUS_STRING_INIT_INVALID;
+
+ if (_dbus_list_length_is_one (sources))
+ {
+ /* If there was exactly one error, just use it */
+ dbus_move_error (_dbus_list_get_first (sources), dest);
+ }
+ else
+ {
+ DBusList *iter;
+ const char *name = NULL;
+
+ /* If there was more than one error, concatenate all the
+ * errors' diagnostic messages, and use their common error
+ * name, or DBUS_ERROR_FAILED if more than one name is
+ * represented */
+ if (!_dbus_string_init (&message))
+ {
+ _DBUS_SET_OOM (dest);
+ goto out;
+ }
+
+ for (iter = _dbus_list_get_first_link (sources);
+ iter != NULL;
+ iter = _dbus_list_get_next_link (sources, iter))
+ {
+ DBusError *error = iter->data;
+
+ if (name == NULL)
+ {
+ /* no error names known yet, try to use this one */
+ name = error->name;
+ }
+ else if (strcmp (name, error->name) != 0)
+ {
+ /* errors of two different names exist, reconcile by
+ * using FAILED */
+ name = DBUS_ERROR_FAILED;
+ }
+
+ if ((_dbus_string_get_length (&message) > 0 &&
+ !_dbus_string_append (&message, "; ")) ||
+ !_dbus_string_append (&message, error->message))
+ {
+ _DBUS_SET_OOM (dest);
+ goto out;
+ }
+ }
+
+ if (name == NULL)
+ name = DBUS_ERROR_FAILED;
+
+ dbus_set_error (dest, name, "%s to \"%s\":%s (%s)",
+ summary, host ? host : "*", port,
+ _dbus_string_get_const_data (&message));
+ }
+
+out:
+ _dbus_string_free (&message);
+}
+
+/** @} end of sysdeps */
+
+/* tests in dbus-sysdeps-util.c */
diff --git a/src/3rdparty/libdbus/dbus/dbus-sysdeps.h b/src/3rdparty/libdbus/dbus/dbus-sysdeps.h
new file mode 100644
index 00000000..27053a43
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-sysdeps.h
@@ -0,0 +1,775 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-sysdeps.h Wrappers around system/libc features (internal to D-Bus implementation)
+ *
+ * Copyright (C) 2002, 2003 Red Hat, Inc.
+ * Copyright (C) 2003 CodeFactory AB
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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
+ *
+ */
+
+#ifndef DBUS_SYSDEPS_H
+#define DBUS_SYSDEPS_H
+
+#ifndef VERSION
+#warning Please include config.h before dbus-sysdeps.h
+#include "config.h"
+#endif
+
+#include <stdint.h>
+
+#ifdef HAVE_INTTYPES_H
+#include <inttypes.h>
+#endif
+
+#ifdef HAVE_STDATOMIC_H
+#include <stdatomic.h>
+#endif
+
+#include <dbus/dbus-errors.h>
+#include <dbus/dbus-file.h>
+#include <dbus/dbus-string.h>
+
+/* this is perhaps bogus, but strcmp() etc. are faster if we use the
+ * stuff straight out of string.h, so have this here for now.
+ */
+#include <string.h>
+#include <stdarg.h>
+
+#if !defined(BROKEN_POLL) && (defined(__APPLE__) || defined(__INTERIX))
+ /* Following libcurl's example, we blacklist poll() on Darwin
+ * (macOS, iOS, etc.) and Interix due to a history of implementation
+ * issues.
+ * https://github.com/curl/curl/blob/master/m4/curl-functions.m4
+ *
+ * On unspecified older macOS versions, poll() failed if given a
+ * device node to poll.
+ *
+ * On macOS < 10.9, poll() with nfds=0 failed instead of waiting for
+ * the timeout and then succeeding.
+ *
+ * On macOS >= 10.12, poll() with nfds=0 succeeded immediately
+ * instead of waiting for the timeout, resulting in busy-looping.
+ *
+ * On Interix, poll() apparently only works for files in /proc.
+ *
+ * The "legacy" build flavour in our CI machinery defines BROKEN_POLL
+ * on whatever platform is in use (normally Linux) to force use of the
+ * same select()-based poll() emulation that we use for macOS, Interix,
+ * and any platform that lacks a real poll(), so that we can test it
+ * more regularly.
+ */
+# define BROKEN_POLL
+#endif
+
+/* Normally we'd only include this in dbus-sysdeps-unix.c.
+ * However, the member names in DBusPollFD are (deliberately) the same as
+ * in POSIX struct pollfd, and AIX's poll() implementation is known to
+ * do things like "#define events reqevents", which would break that approach.
+ * Defend against that by ensuring that if it's renamed anywhere, it's renamed
+ * everywhere.
+ */
+#ifdef HAVE_POLL
+#include <poll.h>
+#endif
+
+#ifdef DBUS_WINCE
+/* Windows CE lacks some system functions (such as errno and clock).
+ We bring them in here. */
+#include "dbus-sysdeps-wince-glue.h"
+#endif
+
+#ifdef DBUS_WIN
+#include <ws2tcpip.h>
+#endif
+
+DBUS_BEGIN_DECLS
+
+#ifdef DBUS_WIN
+#define _DBUS_PATH_SEPARATOR ";"
+#else
+#define _DBUS_PATH_SEPARATOR ":"
+#endif
+
+/* Forward declarations */
+
+
+/** An opaque list type */
+typedef struct DBusList DBusList;
+
+/** Object that contains a list of credentials such as UNIX or Windows user ID */
+typedef struct DBusCredentials DBusCredentials;
+
+/** A wrapper around a pipe descriptor or handle */
+typedef struct DBusPipe DBusPipe;
+
+/**
+ * @addtogroup DBusSysdeps
+ *
+ * @{
+ */
+
+DBUS_PRIVATE_EXPORT
+void _dbus_abort (void) _DBUS_GNUC_NORETURN;
+
+dbus_bool_t _dbus_check_setuid (void);
+DBUS_PRIVATE_EXPORT
+const char* _dbus_getenv (const char *varname);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_clearenv (void);
+char ** _dbus_get_environment (void);
+
+/** A process ID */
+typedef unsigned long dbus_pid_t;
+/** A user ID */
+typedef unsigned long dbus_uid_t;
+/** A group ID */
+typedef unsigned long dbus_gid_t;
+
+/** an invalid PID used to represent an uninitialized dbus_pid_t field */
+#define DBUS_PID_UNSET ((dbus_pid_t) -1)
+/** an invalid UID used to represent an uninitialized dbus_uid_t field */
+#define DBUS_UID_UNSET ((dbus_uid_t) -1)
+/** an invalid GID used to represent an uninitialized dbus_gid_t field */
+#define DBUS_GID_UNSET ((dbus_gid_t) -1)
+
+/** an appropriate printf format for dbus_pid_t */
+#define DBUS_PID_FORMAT "%lu"
+/** an appropriate printf format for dbus_uid_t */
+#define DBUS_UID_FORMAT "%lu"
+/** an appropriate printf format for dbus_gid_t */
+#define DBUS_GID_FORMAT "%lu"
+
+/**
+ * Socket interface
+ */
+#ifdef DBUS_WIN
+
+typedef struct { SOCKET sock; } DBusSocket;
+# define DBUS_SOCKET_FORMAT "Iu"
+# define DBUS_SOCKET_INIT { INVALID_SOCKET }
+
+_DBUS_WARN_UNUSED_RESULT
+static inline SOCKET
+_dbus_socket_printable (DBusSocket s) { return s.sock; }
+
+_DBUS_WARN_UNUSED_RESULT
+static inline dbus_bool_t
+_dbus_socket_is_valid (DBusSocket s) { return s.sock != INVALID_SOCKET; }
+
+static inline void
+_dbus_socket_invalidate (DBusSocket *s) { s->sock = INVALID_SOCKET; }
+
+_DBUS_WARN_UNUSED_RESULT
+static inline int
+_dbus_socket_get_int (DBusSocket s) { return (int)s.sock; }
+
+#else /* not DBUS_WIN */
+
+typedef struct { int fd; } DBusSocket;
+# define DBUS_SOCKET_FORMAT "d"
+# define DBUS_SOCKET_INIT { -1 }
+
+_DBUS_WARN_UNUSED_RESULT
+static inline int
+_dbus_socket_printable (DBusSocket s) { return s.fd; }
+
+_DBUS_WARN_UNUSED_RESULT
+static inline dbus_bool_t
+_dbus_socket_is_valid (DBusSocket s) { return s.fd >= 0; }
+
+static inline void
+_dbus_socket_invalidate (DBusSocket *s) { s->fd = -1; }
+
+_DBUS_WARN_UNUSED_RESULT
+static inline int
+_dbus_socket_get_int (DBusSocket s) { return s.fd; }
+
+#endif /* not DBUS_WIN */
+
+_DBUS_WARN_UNUSED_RESULT
+static inline DBusSocket
+_dbus_socket_get_invalid (void)
+{
+ DBusSocket s = DBUS_SOCKET_INIT;
+
+ return s;
+}
+
+dbus_bool_t _dbus_set_socket_nonblocking (DBusSocket fd,
+ DBusError *error);
+
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_close_socket (DBusSocket *fd,
+ DBusError *error);
+DBUS_PRIVATE_EXPORT
+int _dbus_read_socket (DBusSocket fd,
+ DBusString *buffer,
+ int count);
+DBUS_PRIVATE_EXPORT
+int _dbus_write_socket (DBusSocket fd,
+ const DBusString *buffer,
+ int start,
+ int len);
+int _dbus_write_socket_two (DBusSocket fd,
+ const DBusString *buffer1,
+ int start1,
+ int len1,
+ const DBusString *buffer2,
+ int start2,
+ int len2);
+
+int _dbus_read_socket_with_unix_fds (DBusSocket fd,
+ DBusString *buffer,
+ int count,
+ int *fds,
+ unsigned int *n_fds);
+DBUS_PRIVATE_EXPORT
+int _dbus_write_socket_with_unix_fds (DBusSocket fd,
+ const DBusString *buffer,
+ int start,
+ int len,
+ const int *fds,
+ int n_fds);
+int _dbus_write_socket_with_unix_fds_two (DBusSocket fd,
+ const DBusString *buffer1,
+ int start1,
+ int len1,
+ const DBusString *buffer2,
+ int start2,
+ int len2,
+ const int *fds,
+ int n_fds);
+
+DBusSocket _dbus_connect_tcp_socket (const char *host,
+ const char *port,
+ const char *family,
+ DBusError *error);
+DBusSocket _dbus_connect_tcp_socket_with_nonce (const char *host,
+ const char *port,
+ const char *family,
+ const char *noncefile,
+ DBusError *error);
+int _dbus_listen_tcp_socket (const char *host,
+ const char *port,
+ const char *family,
+ DBusString *retport,
+ const char **retfamily,
+ DBusSocket **fds_p,
+ DBusError *error);
+DBusSocket _dbus_accept (DBusSocket listen_fd);
+
+dbus_bool_t _dbus_read_credentials_socket (DBusSocket client_fd,
+ DBusCredentials *credentials,
+ DBusError *error);
+dbus_bool_t _dbus_send_credentials_socket (DBusSocket server_fd,
+ DBusError *error);
+
+typedef enum
+{
+ DBUS_CREDENTIALS_ADD_FLAGS_USER_DATABASE = (1 << 0),
+ DBUS_CREDENTIALS_ADD_FLAGS_NONE = 0
+} DBusCredentialsAddFlags;
+
+dbus_bool_t _dbus_credentials_add_from_user (DBusCredentials *credentials,
+ const DBusString *username,
+ DBusCredentialsAddFlags flags,
+ DBusError *error);
+
+dbus_bool_t _dbus_credentials_add_from_current_process (DBusCredentials *credentials);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_append_user_from_current_process (DBusString *str);
+
+dbus_bool_t _dbus_parse_unix_user_from_config (const DBusString *username,
+ dbus_uid_t *uid_p);
+dbus_bool_t _dbus_parse_unix_group_from_config (const DBusString *groupname,
+ dbus_gid_t *gid_p);
+dbus_bool_t _dbus_unix_groups_from_uid (dbus_uid_t uid,
+ dbus_gid_t **group_ids,
+ int *n_group_ids,
+ DBusError *error);
+dbus_bool_t _dbus_unix_user_is_at_console (dbus_uid_t uid,
+ DBusError *error);
+dbus_bool_t _dbus_unix_user_is_process_owner (dbus_uid_t uid);
+dbus_bool_t _dbus_windows_user_is_process_owner (const char *windows_sid);
+
+dbus_bool_t _dbus_append_keyring_directory_for_credentials (DBusString *directory,
+ DBusCredentials *credentials);
+
+dbus_bool_t _dbus_daemon_unpublish_session_bus_address (void);
+
+dbus_bool_t _dbus_socket_can_pass_unix_fd(DBusSocket fd);
+
+/* PID FDs are Linux-specific. */
+#ifdef DBUS_WIN
+static inline dbus_pid_t _dbus_resolve_pid_fd (int pid_fd)
+{
+ return DBUS_PID_UNSET;
+}
+
+#else
+DBUS_PRIVATE_EXPORT
+dbus_pid_t _dbus_resolve_pid_fd (int pid_fd);
+#endif
+
+/** Opaque type representing an atomically-modifiable integer
+ * that can be used from multiple threads.
+ */
+typedef struct DBusAtomic DBusAtomic;
+
+/**
+ * An atomic integer safe to increment or decrement from multiple threads.
+ */
+struct DBusAtomic
+{
+#ifdef HAVE_STDATOMIC_H
+ atomic_int value; /**< Value of the atomic integer. */
+#elif defined(DBUS_WIN)
+ volatile long value; /**< Value of the atomic integer. */
+#else
+ volatile dbus_int32_t value; /**< Value of the atomic integer. */
+#endif
+};
+
+DBUS_PRIVATE_EXPORT
+dbus_int32_t _dbus_atomic_inc (DBusAtomic *atomic);
+DBUS_PRIVATE_EXPORT
+dbus_int32_t _dbus_atomic_dec (DBusAtomic *atomic);
+DBUS_PRIVATE_EXPORT
+dbus_int32_t _dbus_atomic_get (DBusAtomic *atomic);
+DBUS_PRIVATE_EXPORT
+void _dbus_atomic_set_zero (DBusAtomic *atomic);
+DBUS_PRIVATE_EXPORT
+void _dbus_atomic_set_nonzero (DBusAtomic *atomic);
+
+#ifdef DBUS_WIN
+
+/* On Windows, you can only poll sockets. We emulate Unix poll() using
+ * select(), so it doesn't matter what precise type we put in DBusPollFD;
+ * use DBusSocket so that the compiler can check we are doing it right.
+ */
+typedef DBusSocket DBusPollable;
+# define DBUS_POLLABLE_FORMAT "Iu"
+
+static inline DBusPollable
+_dbus_socket_get_pollable (DBusSocket s) { return s; }
+
+static inline SOCKET
+_dbus_pollable_printable (DBusPollable p) { return p.sock; }
+
+static inline dbus_bool_t
+_dbus_pollable_is_valid (DBusPollable p) { return _dbus_socket_is_valid (p); }
+
+static inline void
+_dbus_pollable_invalidate (DBusPollable *p) { _dbus_socket_invalidate (p); }
+
+static inline dbus_bool_t
+_dbus_pollable_equals (DBusPollable a, DBusPollable b) { return a.sock == b.sock; }
+
+#else /* !DBUS_WIN */
+
+/* On Unix, you can poll sockets, pipes, etc., and we must put exactly
+ * "int" in DBusPollFD because we're relying on its layout exactly matching
+ * struct pollfd. (This is silly, and one day we should use a better
+ * abstraction.)
+ */
+typedef int DBusPollable;
+# define DBUS_POLLABLE_FORMAT "d"
+
+static inline DBusPollable
+_dbus_socket_get_pollable (DBusSocket s) { return s.fd; }
+
+static inline int
+_dbus_pollable_printable (DBusPollable p) { return p; }
+
+static inline dbus_bool_t
+_dbus_pollable_is_valid (DBusPollable p) { return p >= 0; }
+
+static inline void
+_dbus_pollable_invalidate (DBusPollable *p) { *p = -1; }
+
+static inline dbus_bool_t
+_dbus_pollable_equals (DBusPollable a, DBusPollable b) { return a == b; }
+
+#endif /* !DBUS_WIN */
+
+#if defined(HAVE_POLL) && !defined(BROKEN_POLL)
+/**
+ * A portable struct pollfd wrapper, or an emulation of struct pollfd
+ * on platforms where poll() is missing or broken.
+ */
+typedef struct pollfd DBusPollFD;
+
+/** There is data to read */
+#define _DBUS_POLLIN POLLIN
+/** There is urgent data to read */
+#define _DBUS_POLLPRI POLLPRI
+/** Writing now will not block */
+#define _DBUS_POLLOUT POLLOUT
+/** Error condition */
+#define _DBUS_POLLERR POLLERR
+/** Hung up */
+#define _DBUS_POLLHUP POLLHUP
+/** Invalid request: fd not open */
+#define _DBUS_POLLNVAL POLLNVAL
+#else
+/* Emulate poll() via select(). Because we aren't really going to call
+ * poll(), any similarly-shaped struct is acceptable, and any power of 2
+ * will do for the events/revents; these values happen to match Linux
+ * and *BSD. */
+typedef struct
+{
+ DBusPollable fd; /**< File descriptor */
+ short events; /**< Events to poll for */
+ short revents; /**< Events that occurred */
+} DBusPollFD;
+
+/** There is data to read */
+#define _DBUS_POLLIN 0x0001
+/** There is urgent data to read */
+#define _DBUS_POLLPRI 0x0002
+/** Writing now will not block */
+#define _DBUS_POLLOUT 0x0004
+/** Error condition */
+#define _DBUS_POLLERR 0x0008
+/** Hung up */
+#define _DBUS_POLLHUP 0x0010
+/** Invalid request: fd not open */
+#define _DBUS_POLLNVAL 0x0020
+#endif
+
+DBUS_PRIVATE_EXPORT
+int _dbus_poll (DBusPollFD *fds,
+ int n_fds,
+ int timeout_milliseconds);
+
+DBUS_PRIVATE_EXPORT
+void _dbus_sleep_milliseconds (int milliseconds);
+
+DBUS_PRIVATE_EXPORT
+void _dbus_get_monotonic_time (dbus_int64_t *tv_sec,
+ long *tv_usec);
+
+DBUS_PRIVATE_EXPORT
+void _dbus_get_real_time (dbus_int64_t *tv_sec,
+ long *tv_usec);
+
+/**
+ * directory interface
+ */
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_create_directory (const DBusString *filename,
+ DBusError *error);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_ensure_directory (const DBusString *filename,
+ DBusError *error);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_delete_directory (const DBusString *filename,
+ DBusError *error);
+
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_concat_dir_and_file (DBusString *dir,
+ const DBusString *next_component);
+dbus_bool_t _dbus_string_get_dirname (const DBusString *filename,
+ DBusString *dirname);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_path_is_absolute (const DBusString *filename);
+
+dbus_bool_t _dbus_get_standard_session_servicedirs (DBusList **dirs);
+dbus_bool_t _dbus_get_standard_system_servicedirs (DBusList **dirs);
+dbus_bool_t _dbus_set_up_transient_session_servicedirs (DBusList **dirs,
+ DBusError *error);
+
+dbus_bool_t _dbus_get_system_config_file (DBusString *str);
+dbus_bool_t _dbus_get_session_config_file (DBusString *str);
+
+/** Opaque type for reading a directory listing */
+typedef struct DBusDirIter DBusDirIter;
+
+DBusDirIter* _dbus_directory_open (const DBusString *filename,
+ DBusError *error);
+dbus_bool_t _dbus_directory_get_next_file (DBusDirIter *iter,
+ DBusString *filename,
+ DBusError *error);
+void _dbus_directory_close (DBusDirIter *iter);
+
+dbus_bool_t _dbus_check_dir_is_private_to_user (DBusString *dir,
+ DBusError *error);
+
+DBUS_PRIVATE_EXPORT
+const char* _dbus_get_tmpdir (void);
+
+/**
+ * Random numbers
+ */
+_DBUS_WARN_UNUSED_RESULT
+dbus_bool_t _dbus_generate_random_bytes_buffer (char *buffer,
+ int n_bytes,
+ DBusError *error);
+dbus_bool_t _dbus_generate_random_bytes (DBusString *str,
+ int n_bytes,
+ DBusError *error);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_generate_random_ascii (DBusString *str,
+ int n_bytes,
+ DBusError *error);
+
+DBUS_PRIVATE_EXPORT
+const char* _dbus_error_from_errno (int error_number);
+DBUS_PRIVATE_EXPORT
+const char* _dbus_error_from_system_errno (void);
+
+int _dbus_get_low_level_socket_errno (void);
+
+int _dbus_save_socket_errno (void);
+void _dbus_restore_socket_errno (int saved_errno);
+void _dbus_set_errno_to_zero (void);
+dbus_bool_t _dbus_get_is_errno_eagain_or_ewouldblock (int e);
+dbus_bool_t _dbus_get_is_errno_enomem (int e);
+dbus_bool_t _dbus_get_is_errno_eintr (int e);
+dbus_bool_t _dbus_get_is_errno_epipe (int e);
+dbus_bool_t _dbus_get_is_errno_etoomanyrefs (int e);
+DBUS_PRIVATE_EXPORT
+const char* _dbus_strerror_from_errno (void);
+
+void _dbus_disable_sigpipe (void);
+
+DBUS_PRIVATE_EXPORT
+void _dbus_exit (int code) _DBUS_GNUC_NORETURN;
+
+DBUS_PRIVATE_EXPORT
+int _dbus_printf_string_upper_bound (const char *format,
+ va_list args) _DBUS_GNUC_PRINTF (1, 0);
+
+#ifdef DBUS_ENABLE_VERBOSE_MODE
+DBUS_PRIVATE_EXPORT
+void _dbus_print_thread (void);
+#endif
+
+/**
+ * Portable struct with stat() results
+ */
+typedef struct
+{
+ unsigned long mode; /**< File mode */
+ unsigned long nlink; /**< Number of hard links */
+ dbus_uid_t uid; /**< User owning file */
+ dbus_gid_t gid; /**< Group owning file */
+ unsigned long size; /**< Size of file */
+ unsigned long atime; /**< Access time */
+ unsigned long mtime; /**< Modify time */
+ unsigned long ctime; /**< Creation time */
+} DBusStat;
+
+dbus_bool_t _dbus_stat (const DBusString *filename,
+ DBusStat *statbuf,
+ DBusError *error);
+
+DBusSocket _dbus_connect_unix_socket (const char *path,
+ dbus_bool_t abstract,
+ DBusError *error);
+DBusSocket _dbus_listen_unix_socket (const char *path,
+ dbus_bool_t abstract,
+ DBusError *error);
+
+DBusSocket _dbus_connect_exec (const char *path,
+ char *const argv[],
+ DBusError *error);
+
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_socketpair (DBusSocket *fd1,
+ DBusSocket *fd2,
+ dbus_bool_t blocking,
+ DBusError *error);
+
+DBUS_PRIVATE_EXPORT
+void _dbus_print_backtrace (void);
+
+dbus_bool_t _dbus_become_daemon (const DBusString *pidfile,
+ DBusPipe *print_pid_pipe,
+ DBusError *error,
+ dbus_bool_t keep_umask);
+
+dbus_bool_t _dbus_verify_daemon_user (const char *user);
+dbus_bool_t _dbus_change_to_daemon_user (const char *user,
+ DBusError *error);
+
+dbus_bool_t _dbus_write_pid_to_file_and_pipe (const DBusString *pidfile,
+ DBusPipe *print_pid_pipe,
+ dbus_pid_t pid_to_write,
+ DBusError *error);
+
+dbus_bool_t _dbus_command_for_pid (unsigned long pid,
+ DBusString *str,
+ int max_len,
+ DBusError *error);
+
+typedef enum {
+ DBUS_LOG_FLAGS_STDERR = (1 << 0),
+ DBUS_LOG_FLAGS_SYSTEM_LOG = (1 << 1)
+} DBusLogFlags;
+
+DBUS_PRIVATE_EXPORT
+void _dbus_init_system_log (const char *tag,
+ DBusLogFlags flags);
+
+typedef enum {
+ DBUS_SYSTEM_LOG_INFO,
+ DBUS_SYSTEM_LOG_WARNING,
+ DBUS_SYSTEM_LOG_SECURITY,
+ DBUS_SYSTEM_LOG_ERROR
+} DBusSystemLogSeverity;
+
+DBUS_PRIVATE_EXPORT
+void _dbus_log (DBusSystemLogSeverity severity,
+ const char *msg,
+ ...) _DBUS_GNUC_PRINTF (2, 3);
+DBUS_PRIVATE_EXPORT
+void _dbus_logv (DBusSystemLogSeverity severity,
+ const char *msg,
+ va_list args) _DBUS_GNUC_PRINTF (2, 0);
+
+/** On x86 there is an 80-bit FPU, and if you do "a == b" it may have a
+ * or b in an 80-bit register, thus failing to compare the two 64-bit
+ * doubles for bitwise equality. So this macro compares the two doubles
+ * bitwise.
+ */
+#define _DBUS_DOUBLES_BITWISE_EQUAL(a, b) (memcmp (&(a), &(b), sizeof (double)) == 0)
+
+dbus_bool_t _dbus_get_autolaunch_address (const char *scope,
+ DBusString *address,
+ DBusError *error);
+
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_lookup_session_address (dbus_bool_t *supported,
+ DBusString *address,
+ DBusError *error);
+
+/** Type representing a universally unique ID
+ * @todo rename to UUID instead of GUID
+ */
+typedef union DBusGUID DBusGUID;
+
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_read_local_machine_uuid (DBusGUID *machine_id,
+ dbus_bool_t create_if_not_found,
+ DBusError *error);
+
+/**
+ * Initialize threads as in dbus_threads_init_default(), appropriately
+ * for the platform.
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t _dbus_threads_init_platform_specific (void);
+
+/**
+ * Lock a static mutex used to protect _dbus_threads_init_platform_specific().
+ */
+void _dbus_threads_lock_platform_specific (void);
+
+/**
+ * Undo _dbus_threads_lock_platform_specific().
+ */
+void _dbus_threads_unlock_platform_specific (void);
+
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_split_paths_and_append (DBusString *dirs,
+ const char *suffix,
+ DBusList **dir_list);
+
+unsigned long _dbus_pid_for_log (void);
+
+/* FIXME move back to dbus-sysdeps-unix.h probably -
+ * the PID file handling just needs a little more abstraction
+ * in the bus daemon first.
+ */
+DBUS_PRIVATE_EXPORT
+dbus_pid_t _dbus_getpid (void);
+
+DBUS_PRIVATE_EXPORT
+dbus_uid_t _dbus_getuid (void);
+
+DBUS_PRIVATE_EXPORT
+void _dbus_flush_caches (void);
+
+dbus_bool_t _dbus_replace_install_prefix (DBusString *path);
+
+/* Do not set this too high: it is a denial-of-service risk.
+ * See <https://bugs.freedesktop.org/show_bug.cgi?id=82820>
+ *
+ * (This needs to be in the non-Unix-specific header so that
+ * the config-parser can use it.)
+ */
+#define DBUS_DEFAULT_MESSAGE_UNIX_FDS 16
+
+typedef struct DBusRLimit DBusRLimit;
+
+DBusRLimit *_dbus_rlimit_save_fd_limit (DBusError *error);
+dbus_bool_t _dbus_rlimit_raise_fd_limit (DBusError *error);
+dbus_bool_t _dbus_rlimit_restore_fd_limit (DBusRLimit *saved,
+ DBusError *error);
+void _dbus_rlimit_free (DBusRLimit *lim);
+
+void _dbus_daemon_report_ready (void);
+void _dbus_daemon_report_reloading (void);
+void _dbus_daemon_report_reloaded (void);
+void _dbus_daemon_report_stopping (void);
+
+dbus_bool_t _dbus_inet_sockaddr_to_string (const void *sockaddr_pointer,
+ size_t len,
+ char *string,
+ size_t string_len,
+ const char **family_name,
+ dbus_uint16_t *port,
+ DBusError *error);
+void _dbus_set_error_with_inet_sockaddr (DBusError *error,
+ const void *sockaddr_pointer,
+ size_t len,
+ const char *description,
+ int saved_errno);
+void _dbus_combine_tcp_errors (DBusList **sources,
+ const char *summary,
+ const char *host,
+ const char *port,
+ DBusError *dest);
+
+/**
+ * @def _DBUS_MAX_SUN_PATH_LENGTH
+ *
+ * Maximum length of the path to a UNIX domain socket,
+ * sockaddr_un::sun_path member. POSIX requires that all systems
+ * support at least 100 bytes here, including the nul termination.
+ * We use 99 for the max value to allow for the nul.
+ *
+ * We could probably also do sizeof (addr.sun_path)
+ * but this way we are the same on all platforms
+ * which is probably a good idea.
+ */
+#define _DBUS_MAX_SUN_PATH_LENGTH 99
+
+/** @} */
+
+DBUS_END_DECLS
+
+
+#ifdef DBUS_WIN
+#include "dbus-sysdeps-win.h"
+#endif
+
+#endif /* DBUS_SYSDEPS_H */
diff --git a/src/3rdparty/libdbus/dbus/dbus-test-tap.h b/src/3rdparty/libdbus/dbus/dbus-test-tap.h
new file mode 100644
index 00000000..157942e1
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-test-tap.h
@@ -0,0 +1,66 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-test-tap — TAP helpers for "embedded tests"
+ *
+ * Copyright © 2017 Collabora Ltd.
+ * SPDX-License-Identifier: MIT
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef DBUS_TEST_TAP_H
+#define DBUS_TEST_TAP_H
+
+#include <dbus/dbus-internals.h>
+
+DBUS_EMBEDDED_TESTS_EXPORT
+void _dbus_test_fatal (const char *format,
+ ...) _DBUS_GNUC_NORETURN _DBUS_GNUC_PRINTF (1, 2);
+
+DBUS_EMBEDDED_TESTS_EXPORT
+void _dbus_test_diag (const char *format,
+ ...) _DBUS_GNUC_PRINTF (1, 2);
+
+DBUS_EMBEDDED_TESTS_EXPORT
+void _dbus_test_skip_all (const char *format,
+ ...) _DBUS_GNUC_NORETURN _DBUS_GNUC_PRINTF (1, 2);
+
+DBUS_EMBEDDED_TESTS_EXPORT
+void _dbus_test_ok (const char *format,
+ ...) _DBUS_GNUC_PRINTF (1, 2);
+DBUS_EMBEDDED_TESTS_EXPORT
+void _dbus_test_not_ok (const char *format,
+ ...) _DBUS_GNUC_PRINTF (1, 2);
+DBUS_EMBEDDED_TESTS_EXPORT
+void _dbus_test_skip (const char *format,
+ ...) _DBUS_GNUC_PRINTF (1, 2);
+
+DBUS_EMBEDDED_TESTS_EXPORT
+void _dbus_test_check_memleaks (const char *test_name);
+
+DBUS_EMBEDDED_TESTS_EXPORT
+int _dbus_test_done_testing (void);
+
+#define _dbus_test_check(a) do { \
+ if (!(a)) \
+ _dbus_test_not_ok ("%s:%d - '%s' failed\n", __FILE__, __LINE__, #a); \
+ } while (0)
+
+#endif
diff --git a/src/3rdparty/libdbus/dbus/dbus-test.h b/src/3rdparty/libdbus/dbus/dbus-test.h
new file mode 100644
index 00000000..8eac0041
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-test.h
@@ -0,0 +1,57 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-test.h Declarations of test functions.
+ *
+ * Copyright (C) 2002 Red Hat Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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
+ *
+ */
+
+#ifndef DBUS_TEST_H
+#define DBUS_TEST_H
+
+#include <dbus/dbus-types.h>
+#include <dbus/dbus-string.h>
+#include <dbus/dbus-marshal-validate.h>
+
+/* Only things that are in libdbus-1.la and used from libdbus-internal.la
+ * need to have DBUS_PRIVATE_EXPORT. If you get
+ *
+ * warning: 'foo' redeclared without dllimport attribute: previous
+ * dllimport ignored [-Wattributes]
+ *
+ * then you have added too many.
+ */
+
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_marshal_test (const char *test_data_dir);
+
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_keyring_test (const char *test_data_dir);
+
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_data_slot_test (const char *test_data_dir);
+
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_memory_test (const char *test_data_dir);
+
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_object_tree_test (const char *test_data_dir);
+
+#endif /* DBUS_TEST_H */
diff --git a/src/3rdparty/libdbus/dbus/dbus-threads-internal.h b/src/3rdparty/libdbus/dbus/dbus-threads-internal.h
new file mode 100644
index 00000000..4c7bde63
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-threads-internal.h
@@ -0,0 +1,168 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-threads-internal.h D-Bus thread primitives
+ *
+ * Copyright (C) 2002, 2005 Red Hat Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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
+ *
+ */
+#ifndef DBUS_THREADS_INTERNAL_H
+#define DBUS_THREADS_INTERNAL_H
+
+#include <dbus/dbus-macros.h>
+#include <dbus/dbus-types.h>
+#include <dbus/dbus-threads.h>
+
+/**
+ * @addtogroup DBusThreadsInternals
+ * @{
+ */
+
+/**
+ * A mutex which is recursive if possible, else non-recursive.
+ * This is typically recursive, but that cannot be relied upon.
+ */
+typedef struct DBusRMutex DBusRMutex;
+
+/**
+ * A mutex suitable for use with condition variables.
+ * This is typically non-recursive.
+ */
+typedef struct DBusCMutex DBusCMutex;
+
+/** @} */
+
+DBUS_BEGIN_DECLS
+
+DBUS_PRIVATE_EXPORT
+void _dbus_rmutex_lock (DBusRMutex *mutex);
+DBUS_PRIVATE_EXPORT
+void _dbus_rmutex_unlock (DBusRMutex *mutex);
+void _dbus_rmutex_new_at_location (DBusRMutex **location_p);
+void _dbus_rmutex_free_at_location (DBusRMutex **location_p);
+
+void _dbus_cmutex_lock (DBusCMutex *mutex);
+void _dbus_cmutex_unlock (DBusCMutex *mutex);
+void _dbus_cmutex_new_at_location (DBusCMutex **location_p);
+void _dbus_cmutex_free_at_location (DBusCMutex **location_p);
+
+DBusCondVar* _dbus_condvar_new (void);
+void _dbus_condvar_free (DBusCondVar *cond);
+void _dbus_condvar_wait (DBusCondVar *cond,
+ DBusCMutex *mutex);
+dbus_bool_t _dbus_condvar_wait_timeout (DBusCondVar *cond,
+ DBusCMutex *mutex,
+ int timeout_milliseconds);
+void _dbus_condvar_wake_one (DBusCondVar *cond);
+void _dbus_condvar_new_at_location (DBusCondVar **location_p);
+void _dbus_condvar_free_at_location (DBusCondVar **location_p);
+
+/* Private to threading implementations and dbus-threads.c */
+
+/**
+ * Creates a new mutex which is recursive if possible
+ *
+ * This mutex is used to avoid deadlocking if we hold them while
+ * calling user code.
+ *
+ * @return mutex instance or #NULL on OOM
+ */
+DBUS_EMBEDDED_TESTS_EXPORT
+DBusRMutex *_dbus_platform_rmutex_new (void);
+
+/**
+ * Free a recursive usable mutex
+ *
+ * @param mutex the mutex instance to free
+ */
+DBUS_EMBEDDED_TESTS_EXPORT
+void _dbus_platform_rmutex_free (DBusRMutex *mutex);
+
+/**
+ * Locks a recursively usable mutex
+ *
+ * @param mutex the mutex instance to lock
+ *
+ * Unlike _dbus_cmutex_lock(), it is valid for the same thread
+ * to lock a recursive mutex more than once, and it will not
+ * deadlock. Each call to this function must be paired with a
+ * corresponding call to _dbus_rmutex_unlock().
+ */
+DBUS_EMBEDDED_TESTS_EXPORT
+void _dbus_platform_rmutex_lock (DBusRMutex *mutex);
+
+/**
+ * Release a recursively usable mutex
+ *
+ * @param mutex the mutex instance to release
+ */
+DBUS_EMBEDDED_TESTS_EXPORT
+void _dbus_platform_rmutex_unlock (DBusRMutex *mutex);
+
+/**
+ * Creates a new mutex suitable for use with condition variables
+ *
+ * @return mutex instance or #NULL on OOM
+ */
+DBUS_EMBEDDED_TESTS_EXPORT
+DBusCMutex *_dbus_platform_cmutex_new (void);
+
+/**
+ * Implementation of _dbus_rmutex_new_at_location().
+ * This should only be called internally by the threading implementation.
+ */
+DBUS_EMBEDDED_TESTS_EXPORT
+void _dbus_platform_cmutex_free (DBusCMutex *mutex);
+
+/**
+ * Locks a mutex suitable for use with condition variables
+ *
+ * @param mutex the mutex instance to lock
+ *
+ * @note On Windows, after a thread obtains ownership of a mutex,
+ * it can specify the same mutex in repeated calls to the dbus
+ * platform related mutex lock functions without blocking its
+ * execution. This prevents a thread from deadlocking itself
+ * while waiting for a mutex that it already owns. On unix
+ * like os, calling the dbus platform related mutex lock
+ * functions the second time is a programming error.
+ */
+DBUS_EMBEDDED_TESTS_EXPORT
+void _dbus_platform_cmutex_lock (DBusCMutex *mutex);
+
+/**
+ * Release a mutex suitable for use with condition variables
+ *
+ * @param mutex the mutex instance to release
+ */
+DBUS_EMBEDDED_TESTS_EXPORT
+void _dbus_platform_cmutex_unlock (DBusCMutex *mutex);
+
+DBusCondVar* _dbus_platform_condvar_new (void);
+void _dbus_platform_condvar_free (DBusCondVar *cond);
+void _dbus_platform_condvar_wait (DBusCondVar *cond,
+ DBusCMutex *mutex);
+dbus_bool_t _dbus_platform_condvar_wait_timeout (DBusCondVar *cond,
+ DBusCMutex *mutex,
+ int timeout_milliseconds);
+void _dbus_platform_condvar_wake_one (DBusCondVar *cond);
+
+DBUS_END_DECLS
+
+#endif /* DBUS_THREADS_INTERNAL_H */
diff --git a/src/3rdparty/libdbus/dbus/dbus-threads.c b/src/3rdparty/libdbus/dbus/dbus-threads.c
new file mode 100644
index 00000000..b22cc031
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-threads.c
@@ -0,0 +1,452 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-threads.h D-Bus threads handling
+ *
+ * Copyright (C) 2002, 2003, 2006 Red Hat Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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-threads.h"
+#include "dbus-internals.h"
+#include "dbus-threads-internal.h"
+#include "dbus-list.h"
+
+/* Protected by _dbus_threads_lock_platform_specific() */
+static int thread_init_generation = 0;
+
+/**
+ * @defgroup DBusThreadsInternals Thread functions
+ * @ingroup DBusInternals
+ * @brief _dbus_rmutex_lock(), etc.
+ *
+ * Functions and macros related to threads and thread locks.
+ *
+ * @{
+ */
+
+/**
+ * Creates a new mutex
+ * or creates a no-op mutex if threads are not initialized.
+ * May return #NULL even if threads are initialized, indicating
+ * out-of-memory.
+ *
+ * If possible, the mutex returned by this function is recursive, to
+ * avoid deadlocks. However, that cannot be relied on.
+ *
+ * @param location_p the location of the new mutex, can return #NULL on OOM
+ */
+void
+_dbus_rmutex_new_at_location (DBusRMutex **location_p)
+{
+ _dbus_assert (location_p != NULL);
+
+ if (!dbus_threads_init_default ())
+ {
+ *location_p = NULL;
+ return;
+ }
+
+ *location_p = _dbus_platform_rmutex_new ();
+}
+
+/**
+ * Creates a new mutex
+ * or creates a no-op mutex if threads are not initialized.
+ * May return #NULL even if threads are initialized, indicating
+ * out-of-memory.
+ *
+ * The returned mutex is suitable for use with condition variables.
+ *
+ * @param location_p the location of the new mutex, can return #NULL on OOM
+ */
+void
+_dbus_cmutex_new_at_location (DBusCMutex **location_p)
+{
+ _dbus_assert (location_p != NULL);
+
+ if (!dbus_threads_init_default ())
+ {
+ *location_p = NULL;
+ return;
+ }
+
+ *location_p = _dbus_platform_cmutex_new ();
+}
+
+/**
+ * Frees a DBusRMutex; does nothing if passed a #NULL pointer.
+ */
+void
+_dbus_rmutex_free_at_location (DBusRMutex **location_p)
+{
+ if (location_p == NULL)
+ return;
+
+ if (*location_p != NULL)
+ _dbus_platform_rmutex_free (*location_p);
+}
+
+/**
+ * Frees a DBusCMutex; does nothing if passed a #NULL pointer.
+ */
+void
+_dbus_cmutex_free_at_location (DBusCMutex **location_p)
+{
+ if (location_p == NULL)
+ return;
+
+ if (*location_p != NULL)
+ _dbus_platform_cmutex_free (*location_p);
+}
+
+/**
+ * Locks a mutex. Does nothing if passed a #NULL pointer.
+ * Locks may be recursive if threading implementation initialized
+ * recursive locks.
+ */
+void
+_dbus_rmutex_lock (DBusRMutex *mutex)
+{
+ if (mutex == NULL)
+ return;
+
+ _dbus_platform_rmutex_lock (mutex);
+}
+
+/**
+ * Locks a mutex. Does nothing if passed a #NULL pointer.
+ * Locks may be recursive if threading implementation initialized
+ * recursive locks.
+ */
+void
+_dbus_cmutex_lock (DBusCMutex *mutex)
+{
+ if (mutex == NULL)
+ return;
+
+ _dbus_platform_cmutex_lock (mutex);
+}
+
+/**
+ * Unlocks a mutex. Does nothing if passed a #NULL pointer.
+ *
+ * @returns #TRUE on success
+ */
+void
+_dbus_rmutex_unlock (DBusRMutex *mutex)
+{
+ if (mutex == NULL)
+ return;
+
+ _dbus_platform_rmutex_unlock (mutex);
+}
+
+/**
+ * Unlocks a mutex. Does nothing if passed a #NULL pointer.
+ *
+ * @returns #TRUE on success
+ */
+void
+_dbus_cmutex_unlock (DBusCMutex *mutex)
+{
+ if (mutex == NULL)
+ return;
+
+ _dbus_platform_cmutex_unlock (mutex);
+}
+
+/**
+ * Creates a new condition variable using the function supplied
+ * to dbus_threads_init(), or creates a no-op condition variable
+ * if threads are not initialized. May return #NULL even if
+ * threads are initialized, indicating out-of-memory.
+ *
+ * @returns new mutex or #NULL
+ */
+DBusCondVar *
+_dbus_condvar_new (void)
+{
+ if (!dbus_threads_init_default ())
+ return NULL;
+
+ return _dbus_platform_condvar_new ();
+}
+
+
+/**
+ * This does the same thing as _dbus_condvar_new. It however
+ * gives another level of indirection by allocating a pointer
+ * to point to the condvar location; this used to be useful.
+ *
+ * @returns the location of a new condvar or #NULL on OOM
+ */
+
+void
+_dbus_condvar_new_at_location (DBusCondVar **location_p)
+{
+ _dbus_assert (location_p != NULL);
+
+ *location_p = _dbus_condvar_new();
+}
+
+
+/**
+ * Frees a conditional variable created with dbus_condvar_new(); does
+ * nothing if passed a #NULL pointer.
+ */
+void
+_dbus_condvar_free (DBusCondVar *cond)
+{
+ if (cond == NULL)
+ return;
+
+ _dbus_platform_condvar_free (cond);
+}
+
+/**
+ * Frees a condition variable; does nothing if passed a #NULL pointer.
+ */
+void
+_dbus_condvar_free_at_location (DBusCondVar **location_p)
+{
+ if (location_p == NULL)
+ return;
+
+ if (*location_p != NULL)
+ _dbus_platform_condvar_free (*location_p);
+}
+
+/**
+ * Atomically unlocks the mutex and waits for the conditions
+ * variable to be signalled. Locks the mutex again before
+ * returning.
+ * Does nothing if passed a #NULL pointer.
+ */
+void
+_dbus_condvar_wait (DBusCondVar *cond,
+ DBusCMutex *mutex)
+{
+ if (cond == NULL || mutex == NULL)
+ return;
+
+ _dbus_platform_condvar_wait (cond, mutex);
+}
+
+/**
+ * Atomically unlocks the mutex and waits for the conditions variable
+ * to be signalled, or for a timeout. Locks the mutex again before
+ * returning. Does nothing if passed a #NULL pointer. Return value
+ * is #FALSE if we timed out, #TRUE otherwise.
+ *
+ * @param cond the condition variable
+ * @param mutex the mutex
+ * @param timeout_milliseconds the maximum time to wait
+ * @returns #FALSE if the timeout occurred, #TRUE if not
+ */
+dbus_bool_t
+_dbus_condvar_wait_timeout (DBusCondVar *cond,
+ DBusCMutex *mutex,
+ int timeout_milliseconds)
+{
+ if (cond == NULL || mutex == NULL)
+ return TRUE;
+
+ return _dbus_platform_condvar_wait_timeout (cond, mutex,
+ timeout_milliseconds);
+}
+
+/**
+ * If there are threads waiting on the condition variable, wake
+ * up exactly one.
+ * Does nothing if passed a #NULL pointer.
+ */
+void
+_dbus_condvar_wake_one (DBusCondVar *cond)
+{
+ if (cond == NULL)
+ return;
+
+ _dbus_platform_condvar_wake_one (cond);
+}
+
+/* Protected by _dbus_threads_lock_platform_specific() */
+static DBusRMutex *global_locks[_DBUS_N_GLOBAL_LOCKS] = { NULL };
+
+static void
+shutdown_global_locks (void *nil)
+{
+ int i;
+
+ for (i = 0; i < _DBUS_N_GLOBAL_LOCKS; i++)
+ {
+ _dbus_assert (global_locks[i] != NULL);
+ _dbus_platform_rmutex_free (global_locks[i]);
+ global_locks[i] = NULL;
+ }
+}
+
+static dbus_bool_t
+init_global_locks (void)
+{
+ int i;
+ dbus_bool_t ok;
+
+ for (i = 0; i < _DBUS_N_GLOBAL_LOCKS; i++)
+ {
+ _dbus_assert (global_locks[i] == NULL);
+
+ global_locks[i] = _dbus_platform_rmutex_new ();
+
+ if (global_locks[i] == NULL)
+ goto failed;
+ }
+
+ _dbus_platform_rmutex_lock (global_locks[_DBUS_LOCK_shutdown_funcs]);
+ ok = _dbus_register_shutdown_func_unlocked (shutdown_global_locks, NULL);
+ _dbus_platform_rmutex_unlock (global_locks[_DBUS_LOCK_shutdown_funcs]);
+
+ if (!ok)
+ goto failed;
+
+ return TRUE;
+
+ failed:
+ for (i = i - 1; i >= 0; i--)
+ {
+ _dbus_platform_rmutex_free (global_locks[i]);
+ global_locks[i] = NULL;
+ }
+
+ return FALSE;
+}
+
+dbus_bool_t
+_dbus_lock (DBusGlobalLock lock)
+{
+ _dbus_assert (lock >= 0);
+ _dbus_assert (lock < _DBUS_N_GLOBAL_LOCKS);
+
+ if (thread_init_generation != _dbus_current_generation &&
+ !dbus_threads_init_default ())
+ return FALSE;
+
+ _dbus_platform_rmutex_lock (global_locks[lock]);
+ return TRUE;
+}
+
+void
+_dbus_unlock (DBusGlobalLock lock)
+{
+ _dbus_assert (lock >= 0);
+ _dbus_assert (lock < _DBUS_N_GLOBAL_LOCKS);
+
+ _dbus_platform_rmutex_unlock (global_locks[lock]);
+}
+
+/** @} */ /* end of internals */
+
+/**
+ * @defgroup DBusThreads Thread functions
+ * @ingroup DBus
+ * @brief dbus_threads_init() and dbus_threads_init_default()
+ *
+ * Functions and macros related to threads and thread locks.
+ *
+ * If threads are initialized, the D-Bus library has locks on all
+ * global data structures. In addition, each #DBusConnection has a
+ * lock, so only one thread at a time can touch the connection. (See
+ * @ref DBusConnection for more on connection locking.)
+ *
+ * Most other objects, however, do not have locks - they can only be
+ * used from a single thread at a time, unless you lock them yourself.
+ * For example, a #DBusMessage can't be modified from two threads
+ * at once.
+ *
+ * @{
+ */
+
+/**
+ * Initializes threads, like dbus_threads_init_default().
+ * This version previously allowed user-specified threading
+ * primitives, but since D-Bus 1.6 it ignores them and behaves
+ * exactly like dbus_threads_init_default().
+ *
+ * @param functions ignored, formerly functions for using threads
+ * @returns #TRUE on success, #FALSE if no memory
+ */
+dbus_bool_t
+dbus_threads_init (const DBusThreadFunctions *functions)
+{
+ _dbus_threads_lock_platform_specific ();
+
+ if (thread_init_generation == _dbus_current_generation)
+ {
+ _dbus_threads_unlock_platform_specific ();
+ return TRUE;
+ }
+
+ if (!_dbus_threads_init_platform_specific() ||
+ !init_global_locks ())
+ {
+ _dbus_threads_unlock_platform_specific ();
+ return FALSE;
+ }
+
+ thread_init_generation = _dbus_current_generation;
+
+ _dbus_threads_unlock_platform_specific ();
+ return TRUE;
+}
+
+
+
+/* Default thread implemenation */
+
+/**
+ * Initializes threads. If this function is not called, the D-Bus
+ * library will not lock any data structures. If it is called, D-Bus
+ * will do locking, at some cost in efficiency.
+ *
+ * Since D-Bus 1.7 it is safe to call this function from any thread,
+ * any number of times (but it must be called before any other
+ * libdbus API is used).
+ *
+ * In D-Bus 1.6 or older, this function must be called in the main thread
+ * before any other thread starts. As a result, it is not sufficient to
+ * call this function in a library or plugin, unless the library or plugin
+ * imposes a similar requirement on its callers.
+ *
+ * dbus_shutdown() reverses the effects of this function when it
+ * resets all global state in libdbus.
+ *
+ * @returns #TRUE on success, #FALSE if not enough memory
+ */
+dbus_bool_t
+dbus_threads_init_default (void)
+{
+ return dbus_threads_init (NULL);
+}
+
+
+/** @} */
+
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
+
+#endif /* DBUS_ENABLE_EMBEDDED_TESTS */
diff --git a/src/3rdparty/libdbus/dbus/dbus-threads.h b/src/3rdparty/libdbus/dbus/dbus-threads.h
new file mode 100644
index 00000000..33e1e327
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-threads.h
@@ -0,0 +1,191 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-threads.h D-Bus threads handling
+ *
+ * Copyright (C) 2002 Red Hat Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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
+ *
+ */
+#if !defined (DBUS_INSIDE_DBUS_H) && !defined (DBUS_COMPILATION)
+#error "Only <dbus/dbus.h> can be included directly, this file may disappear or change contents."
+#endif
+
+#ifndef DBUS_THREADS_H
+#define DBUS_THREADS_H
+
+#include <dbus/dbus-macros.h>
+#include <dbus/dbus-types.h>
+
+DBUS_BEGIN_DECLS
+
+/**
+ * @addtogroup DBusThreads
+ * @{
+ */
+
+/** An opaque mutex type provided by the #DBusThreadFunctions implementation installed by dbus_threads_init(). */
+typedef struct DBusMutex DBusMutex;
+/** An opaque condition variable type provided by the #DBusThreadFunctions implementation installed by dbus_threads_init(). */
+typedef struct DBusCondVar DBusCondVar;
+
+/** Deprecated, provide DBusRecursiveMutexNewFunction instead. */
+typedef DBusMutex* (* DBusMutexNewFunction) (void);
+/** Deprecated, provide DBusRecursiveMutexFreeFunction instead. */
+typedef void (* DBusMutexFreeFunction) (DBusMutex *mutex);
+/** Deprecated, provide DBusRecursiveMutexLockFunction instead. Return value is lock success, but gets ignored in practice. */
+typedef dbus_bool_t (* DBusMutexLockFunction) (DBusMutex *mutex);
+/** Deprecated, provide DBusRecursiveMutexUnlockFunction instead. Return value is unlock success, but gets ignored in practice. */
+typedef dbus_bool_t (* DBusMutexUnlockFunction) (DBusMutex *mutex);
+
+/** Creates a new recursively-lockable mutex, or returns #NULL if not
+ * enough memory. Can only fail due to lack of memory. Found in
+ * #DBusThreadFunctions. Do not just use PTHREAD_MUTEX_RECURSIVE for
+ * this, because it does not save/restore the recursion count when
+ * waiting on a condition. libdbus requires the Java-style behavior
+ * where the mutex is fully unlocked to wait on a condition.
+ */
+typedef DBusMutex* (* DBusRecursiveMutexNewFunction) (void);
+/** Frees a recursively-lockable mutex. Found in #DBusThreadFunctions.
+ */
+typedef void (* DBusRecursiveMutexFreeFunction) (DBusMutex *mutex);
+/** Locks a recursively-lockable mutex. Found in #DBusThreadFunctions.
+ * Can only fail due to lack of memory.
+ */
+typedef void (* DBusRecursiveMutexLockFunction) (DBusMutex *mutex);
+/** Unlocks a recursively-lockable mutex. Found in #DBusThreadFunctions.
+ * Can only fail due to lack of memory.
+ */
+typedef void (* DBusRecursiveMutexUnlockFunction) (DBusMutex *mutex);
+
+/** Creates a new condition variable. Found in #DBusThreadFunctions.
+ * Can only fail (returning #NULL) due to lack of memory.
+ */
+typedef DBusCondVar* (* DBusCondVarNewFunction) (void);
+/** Frees a condition variable. Found in #DBusThreadFunctions.
+ */
+typedef void (* DBusCondVarFreeFunction) (DBusCondVar *cond);
+
+/** Waits on a condition variable. Found in
+ * #DBusThreadFunctions. Must work with either a recursive or
+ * nonrecursive mutex, whichever the thread implementation
+ * provides. Note that PTHREAD_MUTEX_RECURSIVE does not work with
+ * condition variables (does not save/restore the recursion count) so
+ * don't try using simply pthread_cond_wait() and a
+ * PTHREAD_MUTEX_RECURSIVE to implement this, it won't work right.
+ *
+ * Has no error conditions. Must succeed if it returns.
+ */
+typedef void (* DBusCondVarWaitFunction) (DBusCondVar *cond,
+ DBusMutex *mutex);
+
+/** Waits on a condition variable with a timeout. Found in
+ * #DBusThreadFunctions. Returns #TRUE if the wait did not
+ * time out, and #FALSE if it did.
+ *
+ * Has no error conditions. Must succeed if it returns.
+ */
+typedef dbus_bool_t (* DBusCondVarWaitTimeoutFunction) (DBusCondVar *cond,
+ DBusMutex *mutex,
+ int timeout_milliseconds);
+/** Wakes one waiting thread on a condition variable. Found in #DBusThreadFunctions.
+ *
+ * Has no error conditions. Must succeed if it returns.
+ */
+typedef void (* DBusCondVarWakeOneFunction) (DBusCondVar *cond);
+
+/** Wakes all waiting threads on a condition variable. Found in #DBusThreadFunctions.
+ *
+ * Has no error conditions. Must succeed if it returns.
+ */
+typedef void (* DBusCondVarWakeAllFunction) (DBusCondVar *cond);
+
+/**
+ * Flags indicating which functions are present in #DBusThreadFunctions. Used to allow
+ * the library to detect older callers of dbus_threads_init() if new possible functions
+ * are added to #DBusThreadFunctions.
+ */
+typedef enum
+{
+ DBUS_THREAD_FUNCTIONS_MUTEX_NEW_MASK = 1 << 0,
+ DBUS_THREAD_FUNCTIONS_MUTEX_FREE_MASK = 1 << 1,
+ DBUS_THREAD_FUNCTIONS_MUTEX_LOCK_MASK = 1 << 2,
+ DBUS_THREAD_FUNCTIONS_MUTEX_UNLOCK_MASK = 1 << 3,
+ DBUS_THREAD_FUNCTIONS_CONDVAR_NEW_MASK = 1 << 4,
+ DBUS_THREAD_FUNCTIONS_CONDVAR_FREE_MASK = 1 << 5,
+ DBUS_THREAD_FUNCTIONS_CONDVAR_WAIT_MASK = 1 << 6,
+ DBUS_THREAD_FUNCTIONS_CONDVAR_WAIT_TIMEOUT_MASK = 1 << 7,
+ DBUS_THREAD_FUNCTIONS_CONDVAR_WAKE_ONE_MASK = 1 << 8,
+ DBUS_THREAD_FUNCTIONS_CONDVAR_WAKE_ALL_MASK = 1 << 9,
+ DBUS_THREAD_FUNCTIONS_RECURSIVE_MUTEX_NEW_MASK = 1 << 10,
+ DBUS_THREAD_FUNCTIONS_RECURSIVE_MUTEX_FREE_MASK = 1 << 11,
+ DBUS_THREAD_FUNCTIONS_RECURSIVE_MUTEX_LOCK_MASK = 1 << 12,
+ DBUS_THREAD_FUNCTIONS_RECURSIVE_MUTEX_UNLOCK_MASK = 1 << 13,
+ DBUS_THREAD_FUNCTIONS_ALL_MASK = (1 << 14) - 1
+} DBusThreadFunctionsMask;
+
+/**
+ * Functions that must be implemented to make the D-Bus library
+ * thread-aware.
+ *
+ * If you supply both recursive and non-recursive mutexes,
+ * libdbus will use the non-recursive version for condition variables,
+ * and the recursive version in other contexts.
+ *
+ * The condition variable functions have to work with nonrecursive
+ * mutexes if you provide those, or with recursive mutexes if you
+ * don't.
+ */
+typedef struct
+{
+ unsigned int mask; /**< Mask indicating which functions are present. */
+
+ DBusMutexNewFunction mutex_new; /**< Function to create a mutex; optional and deprecated. */
+ DBusMutexFreeFunction mutex_free; /**< Function to free a mutex; optional and deprecated. */
+ DBusMutexLockFunction mutex_lock; /**< Function to lock a mutex; optional and deprecated. */
+ DBusMutexUnlockFunction mutex_unlock; /**< Function to unlock a mutex; optional and deprecated. */
+
+ DBusCondVarNewFunction condvar_new; /**< Function to create a condition variable */
+ DBusCondVarFreeFunction condvar_free; /**< Function to free a condition variable */
+ DBusCondVarWaitFunction condvar_wait; /**< Function to wait on a condition */
+ DBusCondVarWaitTimeoutFunction condvar_wait_timeout; /**< Function to wait on a condition with a timeout */
+ DBusCondVarWakeOneFunction condvar_wake_one; /**< Function to wake one thread waiting on the condition */
+ DBusCondVarWakeAllFunction condvar_wake_all; /**< Function to wake all threads waiting on the condition */
+
+ DBusRecursiveMutexNewFunction recursive_mutex_new; /**< Function to create a recursive mutex */
+ DBusRecursiveMutexFreeFunction recursive_mutex_free; /**< Function to free a recursive mutex */
+ DBusRecursiveMutexLockFunction recursive_mutex_lock; /**< Function to lock a recursive mutex */
+ DBusRecursiveMutexUnlockFunction recursive_mutex_unlock; /**< Function to unlock a recursive mutex */
+
+ void (* padding1) (void); /**< Reserved for future expansion */
+ void (* padding2) (void); /**< Reserved for future expansion */
+ void (* padding3) (void); /**< Reserved for future expansion */
+ void (* padding4) (void); /**< Reserved for future expansion */
+
+} DBusThreadFunctions;
+
+DBUS_EXPORT
+dbus_bool_t dbus_threads_init (const DBusThreadFunctions *functions);
+DBUS_EXPORT
+dbus_bool_t dbus_threads_init_default (void);
+
+/** @} */
+
+DBUS_END_DECLS
+
+#endif /* DBUS_THREADS_H */
diff --git a/src/3rdparty/libdbus/dbus/dbus-timeout.c b/src/3rdparty/libdbus/dbus/dbus-timeout.c
new file mode 100644
index 00000000..5ee9808a
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-timeout.c
@@ -0,0 +1,519 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-timeout.c DBusTimeout implementation
+ *
+ * Copyright (C) 2003 CodeFactory AB
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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-timeout.h"
+#include "dbus-list.h"
+
+/**
+ * @defgroup DBusTimeoutInternals DBusTimeout implementation details
+ * @ingroup DBusInternals
+ * @brief implementation details for DBusTimeout
+ *
+ * @{
+ */
+
+/**
+ * Internals of DBusTimeout
+ */
+struct DBusTimeout
+{
+ int refcount; /**< Reference count */
+ int interval; /**< Timeout interval in milliseconds. */
+
+ DBusTimeoutHandler handler; /**< Timeout handler. */
+ void *handler_data; /**< Timeout handler data. */
+ DBusFreeFunction free_handler_data_function; /**< Free the timeout handler data. */
+
+ void *data; /**< Application data. */
+ DBusFreeFunction free_data_function; /**< Free the application data. */
+ unsigned int enabled : 1; /**< True if timeout is active. */
+ unsigned int needs_restart : 1; /**< Flag that timeout should be restarted after re-enabling. */
+};
+
+/**
+ * Creates a new DBusTimeout, enabled by default.
+ * @param interval the timeout interval in milliseconds.
+ * @param handler function to call when the timeout occurs.
+ * @param data data to pass to the handler
+ * @param free_data_function function to be called to free the data.
+ * @returns the new DBusTimeout object,
+ */
+DBusTimeout*
+_dbus_timeout_new (int interval,
+ DBusTimeoutHandler handler,
+ void *data,
+ DBusFreeFunction free_data_function)
+{
+ DBusTimeout *timeout;
+
+ timeout = dbus_new0 (DBusTimeout, 1);
+ if (timeout == NULL)
+ return NULL;
+
+ timeout->refcount = 1;
+ timeout->interval = interval;
+
+ timeout->handler = handler;
+ timeout->handler_data = data;
+ timeout->free_handler_data_function = free_data_function;
+
+ timeout->enabled = TRUE;
+ timeout->needs_restart = FALSE;
+
+ return timeout;
+}
+
+/**
+ * Increments the reference count of a DBusTimeout object.
+ *
+ * @param timeout the timeout object.
+ * @returns the timeout object.
+ */
+DBusTimeout *
+_dbus_timeout_ref (DBusTimeout *timeout)
+{
+ timeout->refcount += 1;
+
+ return timeout;
+}
+
+/**
+ * Decrements the reference count of a DBusTimeout object
+ * and finalizes the object if the count reaches zero.
+ *
+ * @param timeout the timeout object.
+ */
+void
+_dbus_timeout_unref (DBusTimeout *timeout)
+{
+ _dbus_assert (timeout != NULL);
+ _dbus_assert (timeout->refcount > 0);
+
+ timeout->refcount -= 1;
+ if (timeout->refcount == 0)
+ {
+ dbus_timeout_set_data (timeout, NULL, NULL); /* call free_data_function */
+
+ if (timeout->free_handler_data_function)
+ (* timeout->free_handler_data_function) (timeout->handler_data);
+
+ dbus_free (timeout);
+ }
+}
+
+/**
+ * Change the timeout interval to be interval milliseconds from now
+ * (forgetting when the timeout was initially started), and enable it.
+ *
+ * This function is only valid when used in conjunction with DBusLoop:
+ * it can be used in the message bus daemon implementation or in unit tests,
+ * but it cannot be used in conjunction with an application main loop.
+ *
+ * @param timeout the timeout
+ * @param interval the new interval
+ */
+void
+_dbus_timeout_restart (DBusTimeout *timeout,
+ int interval)
+{
+ _dbus_assert (interval >= 0);
+
+ timeout->interval = interval;
+ timeout->enabled = TRUE;
+ timeout->needs_restart = TRUE;
+}
+
+/**
+ * Disable the timeout. Note that you should use
+ * _dbus_connection_toggle_timeout_unlocked() etc. instead, if
+ * the timeout is passed out to an application main loop.
+ * i.e. you can't use this function in the D-Bus library, it's
+ * only used in the message bus daemon implementation.
+ *
+ * @param timeout the timeout
+ * @param enabled #TRUE if timeout should be enabled.
+ */
+void
+_dbus_timeout_disable (DBusTimeout *timeout)
+{
+ timeout->enabled = FALSE;
+}
+
+/**
+ * @typedef DBusTimeoutList
+ *
+ * Opaque data type representing a list of timeouts
+ * and a set of DBusAddTimeoutFunction/DBusRemoveTimeoutFunction.
+ * Automatically handles removing/re-adding timeouts
+ * when the DBusAddTimeoutFunction is updated or changed.
+ * Holds a reference count to each timeout.
+ *
+ */
+
+/**
+ * DBusTimeoutList implementation details. All fields
+ * are private.
+ *
+ */
+struct DBusTimeoutList
+{
+ DBusList *timeouts; /**< Timeout objects. */
+
+ DBusAddTimeoutFunction add_timeout_function; /**< Callback for adding a timeout. */
+ DBusRemoveTimeoutFunction remove_timeout_function; /**< Callback for removing a timeout. */
+ DBusTimeoutToggledFunction timeout_toggled_function; /**< Callback when timeout is enabled/disabled or changes interval */
+ void *timeout_data; /**< Data for timeout callbacks */
+ DBusFreeFunction timeout_free_data_function; /**< Free function for timeout callback data */
+};
+
+/**
+ * Creates a new timeout list. Returns #NULL if insufficient
+ * memory exists.
+ *
+ * @returns the new timeout list, or #NULL on failure.
+ */
+DBusTimeoutList*
+_dbus_timeout_list_new (void)
+{
+ DBusTimeoutList *timeout_list;
+
+ timeout_list = dbus_new0 (DBusTimeoutList, 1);
+ if (timeout_list == NULL)
+ return NULL;
+
+ return timeout_list;
+}
+
+/**
+ * Frees a DBusTimeoutList.
+ *
+ * @param timeout_list the timeout list.
+ */
+void
+_dbus_timeout_list_free (DBusTimeoutList *timeout_list)
+{
+ /* free timeout_data and remove timeouts as a side effect */
+ _dbus_timeout_list_set_functions (timeout_list,
+ NULL, NULL, NULL, NULL, NULL);
+
+ _dbus_list_clear_full (&timeout_list->timeouts,
+ (DBusFreeFunction) _dbus_timeout_unref);
+
+ dbus_free (timeout_list);
+}
+
+/**
+ * Sets the timeout functions. This function is the "backend"
+ * for dbus_connection_set_timeout_functions().
+ *
+ * @param timeout_list the timeout list
+ * @param add_function the add timeout function.
+ * @param remove_function the remove timeout function.
+ * @param toggled_function toggle notify function, or #NULL
+ * @param data the data for those functions.
+ * @param free_data_function the function to free the data.
+ * @returns #FALSE if no memory
+ *
+ */
+dbus_bool_t
+_dbus_timeout_list_set_functions (DBusTimeoutList *timeout_list,
+ DBusAddTimeoutFunction add_function,
+ DBusRemoveTimeoutFunction remove_function,
+ DBusTimeoutToggledFunction toggled_function,
+ void *data,
+ DBusFreeFunction free_data_function)
+{
+ /* Add timeouts with the new function, failing on OOM */
+ if (add_function != NULL)
+ {
+ DBusList *link;
+
+ link = _dbus_list_get_first_link (&timeout_list->timeouts);
+ while (link != NULL)
+ {
+ DBusList *next = _dbus_list_get_next_link (&timeout_list->timeouts,
+ link);
+
+ if (!(* add_function) (link->data, data))
+ {
+ /* remove it all again and return FALSE */
+ DBusList *link2;
+
+ link2 = _dbus_list_get_first_link (&timeout_list->timeouts);
+ while (link2 != link)
+ {
+ DBusList *next2 = _dbus_list_get_next_link (&timeout_list->timeouts,
+ link2);
+
+ (* remove_function) (link2->data, data);
+
+ link2 = next2;
+ }
+
+ return FALSE;
+ }
+
+ link = next;
+ }
+ }
+
+ /* Remove all current timeouts from previous timeout handlers */
+
+ if (timeout_list->remove_timeout_function != NULL)
+ {
+ _dbus_list_foreach (&timeout_list->timeouts,
+ (DBusForeachFunction) timeout_list->remove_timeout_function,
+ timeout_list->timeout_data);
+ }
+
+ if (timeout_list->timeout_free_data_function != NULL)
+ (* timeout_list->timeout_free_data_function) (timeout_list->timeout_data);
+
+ timeout_list->add_timeout_function = add_function;
+ timeout_list->remove_timeout_function = remove_function;
+ timeout_list->timeout_toggled_function = toggled_function;
+ timeout_list->timeout_data = data;
+ timeout_list->timeout_free_data_function = free_data_function;
+
+ return TRUE;
+}
+
+/**
+ * Adds a new timeout to the timeout list, invoking the
+ * application DBusAddTimeoutFunction if appropriate.
+ *
+ * @param timeout_list the timeout list.
+ * @param timeout the timeout to add.
+ * @returns #TRUE on success, #FALSE If no memory.
+ */
+dbus_bool_t
+_dbus_timeout_list_add_timeout (DBusTimeoutList *timeout_list,
+ DBusTimeout *timeout)
+{
+ if (!_dbus_list_append (&timeout_list->timeouts, timeout))
+ return FALSE;
+
+ _dbus_timeout_ref (timeout);
+
+ if (timeout_list->add_timeout_function != NULL)
+ {
+ if (!(* timeout_list->add_timeout_function) (timeout,
+ timeout_list->timeout_data))
+ {
+ _dbus_list_remove_last (&timeout_list->timeouts, timeout);
+ _dbus_timeout_unref (timeout);
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+/**
+ * Removes a timeout from the timeout list, invoking the
+ * application's DBusRemoveTimeoutFunction if appropriate.
+ *
+ * @param timeout_list the timeout list.
+ * @param timeout the timeout to remove.
+ */
+void
+_dbus_timeout_list_remove_timeout (DBusTimeoutList *timeout_list,
+ DBusTimeout *timeout)
+{
+ if (!_dbus_list_remove (&timeout_list->timeouts, timeout))
+ _dbus_assert_not_reached ("Nonexistent timeout was removed");
+
+ if (timeout_list->remove_timeout_function != NULL)
+ (* timeout_list->remove_timeout_function) (timeout,
+ timeout_list->timeout_data);
+
+ _dbus_timeout_unref (timeout);
+}
+
+/**
+ * Sets a timeout to the given enabled state, invoking the
+ * application's DBusTimeoutToggledFunction if appropriate.
+ *
+ * @param timeout_list the timeout list.
+ * @param timeout the timeout to toggle.
+ * @param enabled #TRUE to enable
+ */
+void
+_dbus_timeout_list_toggle_timeout (DBusTimeoutList *timeout_list,
+ DBusTimeout *timeout,
+ dbus_bool_t enabled)
+{
+ enabled = !!enabled;
+
+ if (enabled == timeout->enabled)
+ return;
+
+ timeout->enabled = enabled;
+
+ if (timeout_list->timeout_toggled_function != NULL)
+ (* timeout_list->timeout_toggled_function) (timeout,
+ timeout_list->timeout_data);
+}
+
+/**
+ * Returns whether a timeout needs restart time counting in the event loop.
+ *
+ * @param timeout the DBusTimeout object
+ * @returns #TRUE if restart is needed
+ */
+dbus_bool_t
+_dbus_timeout_needs_restart (DBusTimeout *timeout)
+{
+ return timeout->needs_restart;
+}
+
+/**
+ * Mark timeout as restarted (setting timestamps is responsibility of the event
+ * loop).
+ *
+ * @param timeout the DBusTimeout object
+ */
+void
+_dbus_timeout_restarted (DBusTimeout *timeout)
+{
+ timeout->needs_restart = FALSE;
+}
+
+/** @} */
+
+/**
+ * @defgroup DBusTimeout DBusTimeout
+ * @ingroup DBus
+ * @brief Object representing a timeout
+ *
+ * Types and functions related to DBusTimeout. A timeout
+ * represents a timeout that the main loop needs to monitor,
+ * as in Qt's QTimer or GLib's g_timeout_add().
+ *
+ * Use dbus_connection_set_timeout_functions() or dbus_server_set_timeout_functions()
+ * to be notified when libdbus needs to add or remove timeouts.
+ *
+ * @{
+ */
+
+
+/**
+ * @typedef DBusTimeout
+ *
+ * Opaque object representing a timeout.
+ */
+
+/**
+ * Gets the timeout interval. The dbus_timeout_handle()
+ * should be called each time this interval elapses,
+ * starting after it elapses once.
+ *
+ * The interval may change during the life of the
+ * timeout; if so, the timeout will be disabled and
+ * re-enabled (calling the "timeout toggled function")
+ * to notify you of the change.
+ *
+ * @param timeout the DBusTimeout object.
+ * @returns the interval in milliseconds.
+ */
+int
+dbus_timeout_get_interval (DBusTimeout *timeout)
+{
+ return timeout->interval;
+}
+
+/**
+ * Gets data previously set with dbus_timeout_set_data()
+ * or #NULL if none.
+ *
+ * @param timeout the DBusTimeout object.
+ * @returns previously-set data.
+ */
+void*
+dbus_timeout_get_data (DBusTimeout *timeout)
+{
+ return timeout->data;
+}
+
+/**
+ * Sets data which can be retrieved with dbus_timeout_get_data().
+ * Intended for use by the DBusAddTimeoutFunction and
+ * DBusRemoveTimeoutFunction to store their own data. For example with
+ * Qt you might store the QTimer for this timeout and with GLib
+ * you might store a g_timeout_add result id.
+ *
+ * @param timeout the DBusTimeout object.
+ * @param data the data.
+ * @param free_data_function function to be called to free the data.
+ */
+void
+dbus_timeout_set_data (DBusTimeout *timeout,
+ void *data,
+ DBusFreeFunction free_data_function)
+{
+ if (timeout->free_data_function != NULL)
+ (* timeout->free_data_function) (timeout->data);
+
+ timeout->data = data;
+ timeout->free_data_function = free_data_function;
+}
+
+/**
+ * Calls the timeout handler for this timeout.
+ * This function should be called when the timeout
+ * occurs.
+ *
+ * If this function returns #FALSE, then there wasn't
+ * enough memory to handle the timeout. Typically just
+ * letting the timeout fire again next time it naturally
+ * times out is an adequate response to that problem,
+ * but you could try to do more if you wanted.
+ *
+ * @param timeout the DBusTimeout object.
+ * @returns #FALSE if there wasn't enough memory
+ */
+dbus_bool_t
+dbus_timeout_handle (DBusTimeout *timeout)
+{
+ return (* timeout->handler) (timeout->handler_data);
+}
+
+
+/**
+ * Returns whether a timeout is enabled or not. If not
+ * enabled, it should not be polled by the main loop.
+ *
+ * @param timeout the DBusTimeout object
+ * @returns #TRUE if the timeout is enabled
+ */
+dbus_bool_t
+dbus_timeout_get_enabled (DBusTimeout *timeout)
+{
+ return timeout->enabled;
+}
+
+/** @} end public API docs */
diff --git a/src/3rdparty/libdbus/dbus/dbus-timeout.h b/src/3rdparty/libdbus/dbus/dbus-timeout.h
new file mode 100644
index 00000000..73fdb952
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-timeout.h
@@ -0,0 +1,84 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-timeout.h DBusTimeout internal interfaces
+ *
+ * Copyright (C) 2003 CodeFactory AB
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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
+ *
+ */
+#ifndef DBUS_TIMEOUT_H
+#define DBUS_TIMEOUT_H
+
+#include <dbus/dbus-connection.h>
+#include <dbus/dbus-internals.h>
+
+DBUS_BEGIN_DECLS
+
+/**
+ * @addtogroup DBusTimeoutInternals
+ * @{
+ */
+
+/* Public methods on DBusTimeout are in dbus-connection.h */
+
+typedef struct DBusTimeoutList DBusTimeoutList;
+
+/** function to run when the timeout is handled */
+typedef dbus_bool_t (* DBusTimeoutHandler) (void *data);
+
+DBUS_PRIVATE_EXPORT
+DBusTimeout* _dbus_timeout_new (int interval,
+ DBusTimeoutHandler handler,
+ void *data,
+ DBusFreeFunction free_data_function);
+DBusTimeout* _dbus_timeout_ref (DBusTimeout *timeout);
+DBUS_PRIVATE_EXPORT
+void _dbus_timeout_unref (DBusTimeout *timeout);
+DBUS_PRIVATE_EXPORT
+void _dbus_timeout_restart (DBusTimeout *timeout,
+ int interval);
+DBUS_PRIVATE_EXPORT
+void _dbus_timeout_disable (DBusTimeout *timeout);
+
+DBusTimeoutList *_dbus_timeout_list_new (void);
+void _dbus_timeout_list_free (DBusTimeoutList *timeout_list);
+dbus_bool_t _dbus_timeout_list_set_functions (DBusTimeoutList *timeout_list,
+ DBusAddTimeoutFunction add_function,
+ DBusRemoveTimeoutFunction remove_function,
+ DBusTimeoutToggledFunction toggled_function,
+ void *data,
+ DBusFreeFunction free_data_function);
+dbus_bool_t _dbus_timeout_list_add_timeout (DBusTimeoutList *timeout_list,
+ DBusTimeout *timeout);
+void _dbus_timeout_list_remove_timeout (DBusTimeoutList *timeout_list,
+ DBusTimeout *timeout);
+void _dbus_timeout_list_toggle_timeout (DBusTimeoutList *timeout_list,
+ DBusTimeout *timeout,
+ dbus_bool_t enabled);
+
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_timeout_needs_restart (DBusTimeout *timeout);
+DBUS_PRIVATE_EXPORT
+void _dbus_timeout_restarted (DBusTimeout *timeout);
+
+/** @} */
+
+DBUS_END_DECLS
+
+#endif /* DBUS_TIMEOUT_H */
diff --git a/src/3rdparty/libdbus/dbus/dbus-transport-protected.h b/src/3rdparty/libdbus/dbus/dbus-transport-protected.h
new file mode 100644
index 00000000..136c4031
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-transport-protected.h
@@ -0,0 +1,148 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-transport-protected.h Used by subclasses of DBusTransport object (internal to D-Bus implementation)
+ *
+ * Copyright (C) 2002, 2004 Red Hat Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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
+ *
+ */
+#ifndef DBUS_TRANSPORT_PROTECTED_H
+#define DBUS_TRANSPORT_PROTECTED_H
+
+#include <dbus/dbus-internals.h>
+#include <dbus/dbus-errors.h>
+#include <dbus/dbus-transport.h>
+#include <dbus/dbus-message-internal.h>
+#include <dbus/dbus-auth.h>
+#include <dbus/dbus-resources.h>
+
+DBUS_BEGIN_DECLS
+
+typedef struct DBusTransportVTable DBusTransportVTable;
+
+/**
+ * The virtual table that must be implemented to
+ * create a new kind of transport.
+ */
+struct DBusTransportVTable
+{
+ void (* finalize) (DBusTransport *transport);
+ /**< The finalize method must free the transport. */
+
+ dbus_bool_t (* handle_watch) (DBusTransport *transport,
+ DBusWatch *watch,
+ unsigned int flags);
+ /**< The handle_watch method handles reading/writing
+ * data as indicated by the flags.
+ */
+
+ void (* disconnect) (DBusTransport *transport);
+ /**< Disconnect this transport. */
+
+ dbus_bool_t (* connection_set) (DBusTransport *transport);
+ /**< Called when transport->connection has been filled in */
+
+ void (* do_iteration) (DBusTransport *transport,
+ unsigned int flags,
+ int timeout_milliseconds);
+ /**< Called to do a single "iteration" (block on select/poll
+ * followed by reading or writing data).
+ */
+
+ void (* live_messages_changed) (DBusTransport *transport);
+ /**< Outstanding messages counter changed */
+
+ dbus_bool_t (* get_socket_fd) (DBusTransport *transport,
+ DBusSocket *fd_p);
+ /**< Get socket file descriptor */
+};
+
+/**
+ * Object representing a transport such as a socket.
+ * A transport can shuttle messages from point A to point B,
+ * and is the backend for a #DBusConnection.
+ *
+ */
+struct DBusTransport
+{
+ int refcount; /**< Reference count. */
+
+ const DBusTransportVTable *vtable; /**< Virtual methods for this instance. */
+
+ DBusConnection *connection; /**< Connection owning this transport. */
+
+ DBusMessageLoader *loader; /**< Message-loading buffer. */
+
+ DBusAuth *auth; /**< Authentication conversation */
+
+ DBusCredentials *credentials; /**< Credentials of other end read from the socket */
+
+ long max_live_messages_size; /**< Max total size of received messages. */
+ long max_live_messages_unix_fds; /**< Max total unix fds of received messages. */
+
+ DBusCounter *live_messages; /**< Counter for size/unix fds of all live messages. */
+
+ char *address; /**< Address of the server we are connecting to (#NULL for the server side of a transport) */
+
+ char *expected_guid; /**< GUID we expect the server to have, #NULL on server side or if we don't have an expectation */
+
+ DBusAllowUnixUserFunction unix_user_function; /**< Function for checking whether a user is authorized. */
+ void *unix_user_data; /**< Data for unix_user_function */
+
+ DBusFreeFunction free_unix_user_data; /**< Function to free unix_user_data */
+
+ DBusAllowWindowsUserFunction windows_user_function; /**< Function for checking whether a user is authorized. */
+ void *windows_user_data; /**< Data for windows_user_function */
+
+ DBusFreeFunction free_windows_user_data; /**< Function to free windows_user_data */
+
+ unsigned int disconnected : 1; /**< #TRUE if we are disconnected. */
+ unsigned int authenticated : 1; /**< Cache of auth state; use _dbus_transport_peek_is_authenticated() to query value */
+ unsigned int send_credentials_pending : 1; /**< #TRUE if we need to send credentials */
+ unsigned int receive_credentials_pending : 1; /**< #TRUE if we need to receive credentials */
+ unsigned int is_server : 1; /**< #TRUE if on the server side */
+ unsigned int unused_bytes_recovered : 1; /**< #TRUE if we've recovered unused bytes from auth */
+ unsigned int allow_anonymous : 1; /**< #TRUE if an anonymous client can connect */
+};
+
+dbus_bool_t _dbus_transport_init_base (DBusTransport *transport,
+ const DBusTransportVTable *vtable,
+ const DBusString *server_guid,
+ const DBusString *address);
+void _dbus_transport_finalize_base (DBusTransport *transport);
+
+
+typedef enum
+{
+ DBUS_TRANSPORT_OPEN_NOT_HANDLED, /**< we aren't in charge of this address type */
+ DBUS_TRANSPORT_OPEN_OK, /**< we set up the listen */
+ DBUS_TRANSPORT_OPEN_BAD_ADDRESS, /**< malformed address */
+ DBUS_TRANSPORT_OPEN_DID_NOT_CONNECT /**< well-formed address but failed to set it up */
+} DBusTransportOpenResult;
+
+DBusTransportOpenResult _dbus_transport_open_platform_specific (DBusAddressEntry *entry,
+ DBusTransport **transport_p,
+ DBusError *error);
+
+#define DBUS_TRANSPORT_CAN_SEND_UNIX_FD(x) \
+ _dbus_auth_get_unix_fd_negotiated((x)->auth)
+
+DBUS_END_DECLS
+
+#endif /* DBUS_TRANSPORT_PROTECTED_H */
diff --git a/src/3rdparty/libdbus/dbus/dbus-transport-socket.c b/src/3rdparty/libdbus/dbus/dbus-transport-socket.c
new file mode 100644
index 00000000..d3c2b914
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-transport-socket.c
@@ -0,0 +1,1645 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-transport-socket.c Socket subclasses of DBusTransport
+ *
+ * Copyright (C) 2002, 2003, 2004, 2006 Red Hat Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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 <stdio.h>
+
+#include "dbus-internals.h"
+#include "dbus-connection-internal.h"
+#include "dbus-nonce.h"
+#include "dbus-transport-socket.h"
+#include "dbus-transport-protected.h"
+#include "dbus-watch.h"
+#include "dbus-credentials.h"
+
+/**
+ * @defgroup DBusTransportSocket DBusTransport implementations for sockets
+ * @ingroup DBusInternals
+ * @brief Implementation details of DBusTransport on sockets
+ *
+ * @{
+ */
+
+/**
+ * Opaque object representing a socket file descriptor transport.
+ */
+typedef struct DBusTransportSocket DBusTransportSocket;
+
+/**
+ * Implementation details of DBusTransportSocket. All members are private.
+ */
+struct DBusTransportSocket
+{
+ DBusTransport base; /**< Parent instance */
+ DBusSocket fd; /**< File descriptor. */
+ DBusWatch *read_watch; /**< Watch for readability. */
+ DBusWatch *write_watch; /**< Watch for writability. */
+
+ int max_bytes_read_per_iteration; /**< To avoid blocking too long. */
+ int max_bytes_written_per_iteration; /**< To avoid blocking too long. */
+
+ int message_bytes_written; /**< Number of bytes of current
+ * outgoing message that have
+ * been written.
+ */
+ DBusString encoded_outgoing; /**< Encoded version of current
+ * outgoing message.
+ */
+ DBusString encoded_incoming; /**< Encoded version of current
+ * incoming data.
+ */
+};
+
+static void
+free_watches (DBusTransport *transport)
+{
+ DBusTransportSocket *socket_transport = (DBusTransportSocket*) transport;
+
+ _dbus_verbose ("start\n");
+
+ if (socket_transport->read_watch)
+ {
+ if (transport->connection)
+ _dbus_connection_remove_watch_unlocked (transport->connection,
+ socket_transport->read_watch);
+ _dbus_watch_invalidate (socket_transport->read_watch);
+ _dbus_watch_unref (socket_transport->read_watch);
+ socket_transport->read_watch = NULL;
+ }
+
+ if (socket_transport->write_watch)
+ {
+ if (transport->connection)
+ _dbus_connection_remove_watch_unlocked (transport->connection,
+ socket_transport->write_watch);
+ _dbus_watch_invalidate (socket_transport->write_watch);
+ _dbus_watch_unref (socket_transport->write_watch);
+ socket_transport->write_watch = NULL;
+ }
+
+ _dbus_verbose ("end\n");
+}
+
+static void
+socket_finalize (DBusTransport *transport)
+{
+ DBusTransportSocket *socket_transport = (DBusTransportSocket*) transport;
+
+ _dbus_verbose ("\n");
+
+ free_watches (transport);
+
+ _dbus_string_free (&socket_transport->encoded_outgoing);
+ _dbus_string_free (&socket_transport->encoded_incoming);
+
+ _dbus_transport_finalize_base (transport);
+
+ _dbus_assert (socket_transport->read_watch == NULL);
+ _dbus_assert (socket_transport->write_watch == NULL);
+
+ dbus_free (transport);
+}
+
+static void
+check_write_watch (DBusTransport *transport)
+{
+ DBusTransportSocket *socket_transport = (DBusTransportSocket*) transport;
+ dbus_bool_t needed;
+
+ if (transport->connection == NULL)
+ return;
+
+ if (transport->disconnected)
+ {
+ _dbus_assert (socket_transport->write_watch == NULL);
+ return;
+ }
+
+ _dbus_transport_ref (transport);
+
+ if (_dbus_transport_try_to_authenticate (transport))
+ needed = _dbus_connection_has_messages_to_send_unlocked (transport->connection);
+ else
+ {
+ if (transport->send_credentials_pending)
+ needed = TRUE;
+ else
+ {
+ DBusAuthState auth_state;
+
+ auth_state = _dbus_auth_do_work (transport->auth);
+
+ /* If we need memory we install the write watch just in case,
+ * if there's no need for it, it will get de-installed
+ * next time we try reading.
+ */
+ if (auth_state == DBUS_AUTH_STATE_HAVE_BYTES_TO_SEND ||
+ auth_state == DBUS_AUTH_STATE_WAITING_FOR_MEMORY)
+ needed = TRUE;
+ else
+ needed = FALSE;
+ }
+ }
+
+ _dbus_verbose ("check_write_watch(): needed = %d on connection %p watch %p fd = %" DBUS_SOCKET_FORMAT " outgoing messages exist %d\n",
+ needed, transport->connection, socket_transport->write_watch,
+ _dbus_socket_printable (socket_transport->fd),
+ _dbus_connection_has_messages_to_send_unlocked (transport->connection));
+
+ _dbus_connection_toggle_watch_unlocked (transport->connection,
+ socket_transport->write_watch,
+ needed);
+
+ _dbus_transport_unref (transport);
+}
+
+static void
+check_read_watch (DBusTransport *transport)
+{
+ DBusTransportSocket *socket_transport = (DBusTransportSocket*) transport;
+ dbus_bool_t need_read_watch;
+
+ _dbus_verbose ("fd = %" DBUS_SOCKET_FORMAT "\n",
+ _dbus_socket_printable (socket_transport->fd));
+
+ if (transport->connection == NULL)
+ return;
+
+ if (transport->disconnected)
+ {
+ _dbus_assert (socket_transport->read_watch == NULL);
+ return;
+ }
+
+ _dbus_transport_ref (transport);
+
+ if (_dbus_transport_try_to_authenticate (transport))
+ need_read_watch =
+ (_dbus_counter_get_size_value (transport->live_messages) < transport->max_live_messages_size) &&
+ (_dbus_counter_get_unix_fd_value (transport->live_messages) < transport->max_live_messages_unix_fds);
+ else
+ {
+ if (transport->receive_credentials_pending)
+ need_read_watch = TRUE;
+ else
+ {
+ /* The reason to disable need_read_watch when not WAITING_FOR_INPUT
+ * is to avoid spinning on the file descriptor when we're waiting
+ * to write or for some other part of the auth process
+ */
+ DBusAuthState auth_state;
+
+ auth_state = _dbus_auth_do_work (transport->auth);
+
+ /* If we need memory we install the read watch just in case,
+ * if there's no need for it, it will get de-installed
+ * next time we try reading. If we're authenticated we
+ * install it since we normally have it installed while
+ * authenticated.
+ */
+ if (auth_state == DBUS_AUTH_STATE_WAITING_FOR_INPUT ||
+ auth_state == DBUS_AUTH_STATE_WAITING_FOR_MEMORY ||
+ auth_state == DBUS_AUTH_STATE_AUTHENTICATED)
+ need_read_watch = TRUE;
+ else
+ need_read_watch = FALSE;
+ }
+ }
+
+ _dbus_verbose (" setting read watch enabled = %d\n", need_read_watch);
+ _dbus_connection_toggle_watch_unlocked (transport->connection,
+ socket_transport->read_watch,
+ need_read_watch);
+
+ _dbus_transport_unref (transport);
+}
+
+static void
+do_io_error (DBusTransport *transport)
+{
+ _dbus_transport_ref (transport);
+ _dbus_transport_disconnect (transport);
+ _dbus_transport_unref (transport);
+}
+
+/* return value is whether we successfully read any new data. */
+static dbus_bool_t
+read_data_into_auth (DBusTransport *transport,
+ dbus_bool_t *oom)
+{
+ DBusTransportSocket *socket_transport = (DBusTransportSocket*) transport;
+ DBusString *buffer;
+ int bytes_read;
+ int saved_errno;
+
+ *oom = FALSE;
+
+ _dbus_auth_get_buffer (transport->auth, &buffer);
+
+ bytes_read = _dbus_read_socket (socket_transport->fd,
+ buffer, socket_transport->max_bytes_read_per_iteration);
+ saved_errno = _dbus_save_socket_errno ();
+
+ _dbus_auth_return_buffer (transport->auth, buffer);
+
+ if (bytes_read > 0)
+ {
+ _dbus_verbose (" read %d bytes in auth phase\n", bytes_read);
+
+ return TRUE;
+ }
+ else if (bytes_read < 0)
+ {
+ /* EINTR already handled for us */
+
+ if (_dbus_get_is_errno_enomem (saved_errno))
+ {
+ *oom = TRUE;
+ }
+ else if (_dbus_get_is_errno_eagain_or_ewouldblock (saved_errno))
+ ; /* do nothing, just return FALSE below */
+ else
+ {
+ _dbus_verbose ("Error reading from remote app: %s\n",
+ _dbus_strerror (saved_errno));
+ do_io_error (transport);
+ }
+
+ return FALSE;
+ }
+ else
+ {
+ _dbus_assert (bytes_read == 0);
+
+ _dbus_verbose ("Disconnected from remote app\n");
+ do_io_error (transport);
+
+ return FALSE;
+ }
+}
+
+/* Return value is whether we successfully wrote any bytes */
+static dbus_bool_t
+write_data_from_auth (DBusTransport *transport)
+{
+ DBusTransportSocket *socket_transport = (DBusTransportSocket*) transport;
+ int bytes_written;
+ int saved_errno;
+ const DBusString *buffer;
+
+ if (!_dbus_auth_get_bytes_to_send (transport->auth,
+ &buffer))
+ return FALSE;
+
+ bytes_written = _dbus_write_socket (socket_transport->fd,
+ buffer,
+ 0, _dbus_string_get_length (buffer));
+ saved_errno = _dbus_save_socket_errno ();
+
+ if (bytes_written > 0)
+ {
+ _dbus_auth_bytes_sent (transport->auth, bytes_written);
+ return TRUE;
+ }
+ else if (bytes_written < 0)
+ {
+ /* EINTR already handled for us */
+
+ if (_dbus_get_is_errno_eagain_or_ewouldblock (saved_errno))
+ ;
+ else
+ {
+ _dbus_verbose ("Error writing to remote app: %s\n",
+ _dbus_strerror (saved_errno));
+ do_io_error (transport);
+ }
+ }
+
+ return FALSE;
+}
+
+/* FALSE on OOM */
+static dbus_bool_t
+exchange_credentials (DBusTransport *transport,
+ dbus_bool_t do_reading,
+ dbus_bool_t do_writing)
+{
+ DBusTransportSocket *socket_transport = (DBusTransportSocket*) transport;
+ DBusError error = DBUS_ERROR_INIT;
+
+ _dbus_verbose ("exchange_credentials: do_reading = %d, do_writing = %d\n",
+ do_reading, do_writing);
+
+ if (do_writing && transport->send_credentials_pending)
+ {
+ if (_dbus_send_credentials_socket (socket_transport->fd,
+ &error))
+ {
+ transport->send_credentials_pending = FALSE;
+ }
+ else
+ {
+ _dbus_verbose ("Failed to write credentials: %s\n", error.message);
+ dbus_error_free (&error);
+ do_io_error (transport);
+ }
+ }
+
+ if (do_reading && transport->receive_credentials_pending)
+ {
+ /* FIXME this can fail due to IO error _or_ OOM, broken
+ * (somewhat tricky to fix since the OOM error can be set after
+ * we already read the credentials byte, so basically we need to
+ * separate reading the byte and storing it in the
+ * transport->credentials). Does not really matter for now
+ * because storing in credentials never actually fails on unix.
+ */
+ if (_dbus_read_credentials_socket (socket_transport->fd,
+ transport->credentials,
+ &error))
+ {
+ transport->receive_credentials_pending = FALSE;
+ }
+ else
+ {
+ _dbus_verbose ("Failed to read credentials %s\n", error.message);
+ dbus_error_free (&error);
+ do_io_error (transport);
+ }
+ }
+
+ if (!(transport->send_credentials_pending ||
+ transport->receive_credentials_pending))
+ {
+ if (!_dbus_auth_set_credentials (transport->auth,
+ transport->credentials))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static dbus_bool_t
+do_authentication (DBusTransport *transport,
+ dbus_bool_t do_reading,
+ dbus_bool_t do_writing,
+ dbus_bool_t *auth_completed)
+{
+ dbus_bool_t oom;
+ dbus_bool_t orig_auth_state;
+
+ oom = FALSE;
+
+ orig_auth_state = _dbus_transport_try_to_authenticate (transport);
+
+ /* This is essential to avoid the check_write_watch() at the end,
+ * we don't want to add a write watch in do_iteration before
+ * we try writing and get EAGAIN
+ */
+ if (orig_auth_state)
+ {
+ if (auth_completed)
+ *auth_completed = FALSE;
+ return TRUE;
+ }
+
+ _dbus_transport_ref (transport);
+
+ while (!_dbus_transport_try_to_authenticate (transport) &&
+ _dbus_transport_get_is_connected (transport))
+ {
+ if (!exchange_credentials (transport, do_reading, do_writing))
+ {
+ /* OOM */
+ oom = TRUE;
+ goto out;
+ }
+
+ if (transport->send_credentials_pending ||
+ transport->receive_credentials_pending)
+ {
+ _dbus_verbose ("send_credentials_pending = %d receive_credentials_pending = %d\n",
+ transport->send_credentials_pending,
+ transport->receive_credentials_pending);
+ goto out;
+ }
+
+#define TRANSPORT_SIDE(t) ((t)->is_server ? "server" : "client")
+ switch (_dbus_auth_do_work (transport->auth))
+ {
+ case DBUS_AUTH_STATE_WAITING_FOR_INPUT:
+ _dbus_verbose (" %s auth state: waiting for input\n",
+ TRANSPORT_SIDE (transport));
+ if (!do_reading || !read_data_into_auth (transport, &oom))
+ goto out;
+ break;
+
+ case DBUS_AUTH_STATE_WAITING_FOR_MEMORY:
+ _dbus_verbose (" %s auth state: waiting for memory\n",
+ TRANSPORT_SIDE (transport));
+ oom = TRUE;
+ goto out;
+ break;
+
+ case DBUS_AUTH_STATE_HAVE_BYTES_TO_SEND:
+ _dbus_verbose (" %s auth state: bytes to send\n",
+ TRANSPORT_SIDE (transport));
+ if (!do_writing || !write_data_from_auth (transport))
+ goto out;
+ break;
+
+ case DBUS_AUTH_STATE_NEED_DISCONNECT:
+ _dbus_verbose (" %s auth state: need to disconnect\n",
+ TRANSPORT_SIDE (transport));
+ do_io_error (transport);
+ break;
+
+ case DBUS_AUTH_STATE_AUTHENTICATED:
+ _dbus_verbose (" %s auth state: authenticated\n",
+ TRANSPORT_SIDE (transport));
+ break;
+
+ case DBUS_AUTH_STATE_INVALID:
+ /* fall through */
+ default:
+ _dbus_assert_not_reached ("invalid auth state");
+ }
+ }
+
+ out:
+ if (auth_completed)
+ *auth_completed = (orig_auth_state != _dbus_transport_try_to_authenticate (transport));
+
+ check_read_watch (transport);
+ check_write_watch (transport);
+ _dbus_transport_unref (transport);
+
+ if (oom)
+ return FALSE;
+ else
+ return TRUE;
+}
+
+/* returns false on oom */
+static dbus_bool_t
+do_writing (DBusTransport *transport)
+{
+ int total;
+ DBusTransportSocket *socket_transport = (DBusTransportSocket*) transport;
+ dbus_bool_t oom;
+
+ /* No messages without authentication! */
+ if (!_dbus_transport_try_to_authenticate (transport))
+ {
+ _dbus_verbose ("Not authenticated, not writing anything\n");
+ return TRUE;
+ }
+
+ if (transport->disconnected)
+ {
+ _dbus_verbose ("Not connected, not writing anything\n");
+ return TRUE;
+ }
+
+#if 1
+ _dbus_verbose ("do_writing(), have_messages = %d, fd = %" DBUS_SOCKET_FORMAT "\n",
+ _dbus_connection_has_messages_to_send_unlocked (transport->connection),
+ _dbus_socket_printable (socket_transport->fd));
+#endif
+
+ oom = FALSE;
+ total = 0;
+
+ while (!transport->disconnected &&
+ _dbus_connection_has_messages_to_send_unlocked (transport->connection))
+ {
+ int bytes_written;
+ DBusMessage *message;
+ const DBusString *header;
+ const DBusString *body;
+ int header_len, body_len;
+ int total_bytes_to_write;
+ int saved_errno;
+
+ if (total > socket_transport->max_bytes_written_per_iteration)
+ {
+ _dbus_verbose ("%d bytes exceeds %d bytes written per iteration, returning\n",
+ total, socket_transport->max_bytes_written_per_iteration);
+ goto out;
+ }
+
+ message = _dbus_connection_get_message_to_send (transport->connection);
+ _dbus_assert (message != NULL);
+ dbus_message_lock (message);
+
+#if 0
+ _dbus_verbose ("writing message %p\n", message);
+#endif
+
+ _dbus_message_get_network_data (message,
+ &header, &body);
+
+ header_len = _dbus_string_get_length (header);
+ body_len = _dbus_string_get_length (body);
+
+ if (_dbus_auth_needs_encoding (transport->auth))
+ {
+ /* Does fd passing even make sense with encoded data? */
+ _dbus_assert(!DBUS_TRANSPORT_CAN_SEND_UNIX_FD(transport));
+
+ if (_dbus_string_get_length (&socket_transport->encoded_outgoing) == 0)
+ {
+ if (!_dbus_auth_encode_data (transport->auth,
+ header, &socket_transport->encoded_outgoing))
+ {
+ oom = TRUE;
+ goto out;
+ }
+
+ if (!_dbus_auth_encode_data (transport->auth,
+ body, &socket_transport->encoded_outgoing))
+ {
+ _dbus_string_set_length (&socket_transport->encoded_outgoing, 0);
+ oom = TRUE;
+ goto out;
+ }
+ }
+
+ total_bytes_to_write = _dbus_string_get_length (&socket_transport->encoded_outgoing);
+
+#if 0
+ _dbus_verbose ("encoded message is %d bytes\n",
+ total_bytes_to_write);
+#endif
+
+ bytes_written =
+ _dbus_write_socket (socket_transport->fd,
+ &socket_transport->encoded_outgoing,
+ socket_transport->message_bytes_written,
+ total_bytes_to_write - socket_transport->message_bytes_written);
+ saved_errno = _dbus_save_socket_errno ();
+ }
+ else
+ {
+ total_bytes_to_write = header_len + body_len;
+
+#if 0
+ _dbus_verbose ("message is %d bytes\n",
+ total_bytes_to_write);
+#endif
+
+#ifdef HAVE_UNIX_FD_PASSING
+ if (socket_transport->message_bytes_written <= 0 && DBUS_TRANSPORT_CAN_SEND_UNIX_FD(transport))
+ {
+ /* Send the fds along with the first byte of the message */
+ const int *unix_fds;
+ unsigned n;
+
+ _dbus_message_get_unix_fds(message, &unix_fds, &n);
+
+ bytes_written =
+ _dbus_write_socket_with_unix_fds_two (socket_transport->fd,
+ header,
+ socket_transport->message_bytes_written,
+ header_len - socket_transport->message_bytes_written,
+ body,
+ 0, body_len,
+ unix_fds,
+ n);
+ saved_errno = _dbus_save_socket_errno ();
+
+ if (bytes_written > 0 && n > 0)
+ _dbus_verbose("Wrote %i unix fds\n", n);
+ }
+ else
+#endif
+ {
+ if (socket_transport->message_bytes_written < header_len)
+ {
+ bytes_written =
+ _dbus_write_socket_two (socket_transport->fd,
+ header,
+ socket_transport->message_bytes_written,
+ header_len - socket_transport->message_bytes_written,
+ body,
+ 0, body_len);
+ }
+ else
+ {
+ bytes_written =
+ _dbus_write_socket (socket_transport->fd,
+ body,
+ (socket_transport->message_bytes_written - header_len),
+ body_len -
+ (socket_transport->message_bytes_written - header_len));
+ }
+
+ saved_errno = _dbus_save_socket_errno ();
+ }
+ }
+
+ if (bytes_written < 0)
+ {
+ /* EINTR already handled for us */
+
+ /* If the other end closed the socket with close() or shutdown(), we
+ * receive EPIPE here but we must not close the socket yet: there
+ * might still be some data to read. See:
+ * http://lists.freedesktop.org/archives/dbus/2008-March/009526.html
+ */
+
+ if (_dbus_get_is_errno_eagain_or_ewouldblock (saved_errno) || _dbus_get_is_errno_epipe (saved_errno))
+ goto out;
+
+ /* Since Linux commit 25888e (from 2.6.37-rc4, Nov 2010), sendmsg()
+ * on Unix sockets returns -1 errno=ETOOMANYREFS when the passfd
+ * mechanism (SCM_RIGHTS) is used recursively with a recursion level
+ * of maximum 4. The kernel does not have an API to check whether
+ * the passed fds can be forwarded and it can change asynchronously.
+ * See:
+ * https://bugs.freedesktop.org/show_bug.cgi?id=80163
+ */
+
+ else if (_dbus_get_is_errno_etoomanyrefs (saved_errno))
+ {
+ /* We only send fds in the first byte of the message.
+ * ETOOMANYREFS cannot happen after.
+ */
+ _dbus_assert (socket_transport->message_bytes_written == 0);
+
+ _dbus_verbose (" discard message of %d bytes due to ETOOMANYREFS\n",
+ total_bytes_to_write);
+
+ socket_transport->message_bytes_written = 0;
+ _dbus_string_set_length (&socket_transport->encoded_outgoing, 0);
+ _dbus_string_compact (&socket_transport->encoded_outgoing, 2048);
+
+ /* The message was not actually sent but it needs to be removed
+ * from the outgoing queue
+ */
+ _dbus_connection_message_sent_unlocked (transport->connection,
+ message);
+ }
+ else
+ {
+ _dbus_verbose ("Error writing to remote app: %s\n",
+ _dbus_strerror (saved_errno));
+ do_io_error (transport);
+ goto out;
+ }
+ }
+ else
+ {
+ _dbus_verbose (" wrote %d bytes of %d\n", bytes_written,
+ total_bytes_to_write);
+
+ total += bytes_written;
+ socket_transport->message_bytes_written += bytes_written;
+
+ _dbus_assert (socket_transport->message_bytes_written <=
+ total_bytes_to_write);
+
+ if (socket_transport->message_bytes_written == total_bytes_to_write)
+ {
+ socket_transport->message_bytes_written = 0;
+ _dbus_string_set_length (&socket_transport->encoded_outgoing, 0);
+ _dbus_string_compact (&socket_transport->encoded_outgoing, 2048);
+
+ _dbus_connection_message_sent_unlocked (transport->connection,
+ message);
+ }
+ }
+ }
+
+ out:
+ if (oom)
+ return FALSE;
+ else
+ return TRUE;
+}
+
+/* returns false on out-of-memory */
+static dbus_bool_t
+do_reading (DBusTransport *transport)
+{
+ DBusTransportSocket *socket_transport = (DBusTransportSocket*) transport;
+ DBusString *buffer;
+ int bytes_read;
+ int total;
+ dbus_bool_t oom;
+ int saved_errno;
+
+ _dbus_verbose ("fd = %" DBUS_SOCKET_FORMAT "\n",
+ _dbus_socket_printable (socket_transport->fd));
+
+ /* No messages without authentication! */
+ if (!_dbus_transport_try_to_authenticate (transport))
+ return TRUE;
+
+ oom = FALSE;
+
+ total = 0;
+
+ again:
+
+ /* See if we've exceeded max messages and need to disable reading */
+ check_read_watch (transport);
+
+ if (total > socket_transport->max_bytes_read_per_iteration)
+ {
+ _dbus_verbose ("%d bytes exceeds %d bytes read per iteration, returning\n",
+ total, socket_transport->max_bytes_read_per_iteration);
+ goto out;
+ }
+
+ _dbus_assert (socket_transport->read_watch != NULL ||
+ transport->disconnected);
+
+ if (transport->disconnected)
+ goto out;
+
+ if (!dbus_watch_get_enabled (socket_transport->read_watch))
+ return TRUE;
+
+ if (_dbus_auth_needs_decoding (transport->auth))
+ {
+ /* Does fd passing even make sense with encoded data? */
+ _dbus_assert(!DBUS_TRANSPORT_CAN_SEND_UNIX_FD(transport));
+
+ if (_dbus_string_get_length (&socket_transport->encoded_incoming) > 0)
+ bytes_read = _dbus_string_get_length (&socket_transport->encoded_incoming);
+ else
+ bytes_read = _dbus_read_socket (socket_transport->fd,
+ &socket_transport->encoded_incoming,
+ socket_transport->max_bytes_read_per_iteration);
+
+ saved_errno = _dbus_save_socket_errno ();
+
+ _dbus_assert (_dbus_string_get_length (&socket_transport->encoded_incoming) ==
+ bytes_read);
+
+ if (bytes_read > 0)
+ {
+ _dbus_message_loader_get_buffer (transport->loader,
+ &buffer,
+ NULL,
+ NULL);
+
+ if (!_dbus_auth_decode_data (transport->auth,
+ &socket_transport->encoded_incoming,
+ buffer))
+ {
+ _dbus_verbose ("Out of memory decoding incoming data\n");
+ _dbus_message_loader_return_buffer (transport->loader,
+ buffer);
+
+ oom = TRUE;
+ goto out;
+ }
+
+ _dbus_message_loader_return_buffer (transport->loader,
+ buffer);
+
+ _dbus_string_set_length (&socket_transport->encoded_incoming, 0);
+ _dbus_string_compact (&socket_transport->encoded_incoming, 2048);
+ }
+ }
+ else
+ {
+ int max_to_read = DBUS_MAXIMUM_MESSAGE_LENGTH;
+ dbus_bool_t may_read_unix_fds = TRUE;
+
+ _dbus_message_loader_get_buffer (transport->loader,
+ &buffer,
+ &max_to_read,
+ &may_read_unix_fds);
+
+ if (max_to_read > socket_transport->max_bytes_read_per_iteration)
+ max_to_read = socket_transport->max_bytes_read_per_iteration;
+
+#ifdef HAVE_UNIX_FD_PASSING
+ if (DBUS_TRANSPORT_CAN_SEND_UNIX_FD(transport) && may_read_unix_fds)
+ {
+ int *fds;
+ unsigned int n_fds;
+
+ if (!_dbus_message_loader_get_unix_fds(transport->loader, &fds, &n_fds))
+ {
+ _dbus_verbose ("Out of memory reading file descriptors\n");
+ _dbus_message_loader_return_buffer (transport->loader, buffer);
+ oom = TRUE;
+ goto out;
+ }
+
+ bytes_read = _dbus_read_socket_with_unix_fds(socket_transport->fd,
+ buffer,
+ max_to_read,
+ fds, &n_fds);
+ saved_errno = _dbus_save_socket_errno ();
+
+ if (bytes_read >= 0 && n_fds > 0)
+ _dbus_verbose("Read %i unix fds\n", n_fds);
+
+ _dbus_message_loader_return_unix_fds(transport->loader, fds, bytes_read < 0 ? 0 : n_fds);
+ }
+ else
+#endif
+ {
+ bytes_read = _dbus_read_socket (socket_transport->fd,
+ buffer, max_to_read);
+ saved_errno = _dbus_save_socket_errno ();
+ }
+
+ _dbus_message_loader_return_buffer (transport->loader,
+ buffer);
+ }
+
+ if (bytes_read < 0)
+ {
+ /* EINTR already handled for us */
+
+ if (_dbus_get_is_errno_enomem (saved_errno))
+ {
+ _dbus_verbose ("Out of memory in read()/do_reading()\n");
+ oom = TRUE;
+ goto out;
+ }
+ else if (_dbus_get_is_errno_eagain_or_ewouldblock (saved_errno))
+ goto out;
+ else
+ {
+ _dbus_verbose ("Error reading from remote app: %s\n",
+ _dbus_strerror (saved_errno));
+ do_io_error (transport);
+ goto out;
+ }
+ }
+ else if (bytes_read == 0)
+ {
+ _dbus_verbose ("Disconnected from remote app\n");
+ do_io_error (transport);
+ goto out;
+ }
+ else
+ {
+ _dbus_verbose (" read %d bytes\n", bytes_read);
+
+ total += bytes_read;
+
+ if (!_dbus_transport_queue_messages (transport))
+ {
+ oom = TRUE;
+ _dbus_verbose (" out of memory when queueing messages we just read in the transport\n");
+ goto out;
+ }
+
+ /* Try reading more data until we get EAGAIN and return, or
+ * exceed max bytes per iteration. If in blocking mode of
+ * course we'll block instead of returning.
+ */
+ goto again;
+ }
+
+ out:
+ if (oom)
+ return FALSE;
+ else
+ return TRUE;
+}
+
+static dbus_bool_t
+unix_error_with_read_to_come (DBusTransport *itransport,
+ DBusWatch *watch,
+ unsigned int flags)
+{
+ DBusTransportSocket *transport = (DBusTransportSocket *) itransport;
+
+ if (!(flags & DBUS_WATCH_HANGUP || flags & DBUS_WATCH_ERROR))
+ return FALSE;
+
+ /* If we have a read watch enabled ...
+ we -might have data incoming ... => handle the HANGUP there */
+ if (watch != transport->read_watch &&
+ _dbus_watch_get_enabled (transport->read_watch))
+ return FALSE;
+
+ return TRUE;
+}
+
+static dbus_bool_t
+socket_handle_watch (DBusTransport *transport,
+ DBusWatch *watch,
+ unsigned int flags)
+{
+ DBusTransportSocket *socket_transport = (DBusTransportSocket*) transport;
+
+ _dbus_assert (watch == socket_transport->read_watch ||
+ watch == socket_transport->write_watch);
+ _dbus_assert (watch != NULL);
+
+ /* If we hit an error here on a write watch, don't disconnect the transport yet because data can
+ * still be in the buffer and do_reading may need several iteration to read
+ * it all (because of its max_bytes_read_per_iteration limit).
+ */
+ if (!(flags & DBUS_WATCH_READABLE) && unix_error_with_read_to_come (transport, watch, flags))
+ {
+ _dbus_verbose ("Hang up or error on watch\n");
+ _dbus_transport_disconnect (transport);
+ return TRUE;
+ }
+
+ if (watch == socket_transport->read_watch &&
+ (flags & DBUS_WATCH_READABLE))
+ {
+ dbus_bool_t auth_finished;
+#if 1
+ _dbus_verbose ("handling read watch %p flags = %x\n",
+ watch, flags);
+#endif
+ if (!do_authentication (transport, TRUE, FALSE, &auth_finished))
+ return FALSE;
+
+ /* We don't want to do a read immediately following
+ * a successful authentication. This is so we
+ * have a chance to propagate the authentication
+ * state further up. Specifically, we need to
+ * process any pending data from the auth object.
+ */
+ if (!auth_finished)
+ {
+ if (!do_reading (transport))
+ {
+ _dbus_verbose ("no memory to read\n");
+ return FALSE;
+ }
+ }
+ else
+ {
+ _dbus_verbose ("Not reading anything since we just completed the authentication\n");
+ }
+ }
+ else if (watch == socket_transport->write_watch &&
+ (flags & DBUS_WATCH_WRITABLE))
+ {
+#if 1
+ _dbus_verbose ("handling write watch, have_outgoing_messages = %d\n",
+ _dbus_connection_has_messages_to_send_unlocked (transport->connection));
+#endif
+ if (!do_authentication (transport, FALSE, TRUE, NULL))
+ return FALSE;
+
+ if (!do_writing (transport))
+ {
+ _dbus_verbose ("no memory to write\n");
+ return FALSE;
+ }
+
+ /* See if we still need the write watch */
+ check_write_watch (transport);
+ }
+#ifdef DBUS_ENABLE_VERBOSE_MODE
+ else
+ {
+ if (watch == socket_transport->read_watch)
+ _dbus_verbose ("asked to handle read watch with non-read condition 0x%x\n",
+ flags);
+ else if (watch == socket_transport->write_watch)
+ _dbus_verbose ("asked to handle write watch with non-write condition 0x%x\n",
+ flags);
+ else
+ _dbus_verbose ("asked to handle watch %p on fd %" DBUS_SOCKET_FORMAT " that we don't recognize\n",
+ watch, _dbus_socket_printable (_dbus_watch_get_socket (watch)));
+ }
+#endif /* DBUS_ENABLE_VERBOSE_MODE */
+
+ return TRUE;
+}
+
+static void
+socket_disconnect (DBusTransport *transport)
+{
+ DBusTransportSocket *socket_transport = (DBusTransportSocket*) transport;
+
+ _dbus_verbose ("\n");
+
+ free_watches (transport);
+
+ _dbus_close_socket (&socket_transport->fd, NULL);
+}
+
+static dbus_bool_t
+socket_connection_set (DBusTransport *transport)
+{
+ DBusTransportSocket *socket_transport = (DBusTransportSocket*) transport;
+
+ _dbus_watch_set_handler (socket_transport->write_watch,
+ _dbus_connection_handle_watch,
+ transport->connection, NULL);
+
+ _dbus_watch_set_handler (socket_transport->read_watch,
+ _dbus_connection_handle_watch,
+ transport->connection, NULL);
+
+ if (!_dbus_connection_add_watch_unlocked (transport->connection,
+ socket_transport->write_watch))
+ return FALSE;
+
+ if (!_dbus_connection_add_watch_unlocked (transport->connection,
+ socket_transport->read_watch))
+ {
+ _dbus_connection_remove_watch_unlocked (transport->connection,
+ socket_transport->write_watch);
+ return FALSE;
+ }
+
+ check_read_watch (transport);
+ check_write_watch (transport);
+
+ return TRUE;
+}
+
+/**
+ * @todo We need to have a way to wake up the select sleep if
+ * a new iteration request comes in with a flag (read/write) that
+ * we're not currently serving. Otherwise a call that just reads
+ * could block a write call forever (if there are no incoming
+ * messages).
+ */
+static void
+socket_do_iteration (DBusTransport *transport,
+ unsigned int flags,
+ int timeout_milliseconds)
+{
+ DBusTransportSocket *socket_transport = (DBusTransportSocket*) transport;
+ DBusPollFD poll_fd;
+ int poll_res;
+ int poll_timeout;
+
+ _dbus_verbose (" iteration flags = %s%s timeout = %d read_watch = %p write_watch = %p fd = %" DBUS_SOCKET_FORMAT "\n",
+ flags & DBUS_ITERATION_DO_READING ? "read" : "",
+ flags & DBUS_ITERATION_DO_WRITING ? "write" : "",
+ timeout_milliseconds,
+ socket_transport->read_watch,
+ socket_transport->write_watch,
+ _dbus_socket_printable (socket_transport->fd));
+
+ /* the passed in DO_READING/DO_WRITING flags indicate whether to
+ * read/write messages, but regardless of those we may need to block
+ * for reading/writing to do auth. But if we do reading for auth,
+ * we don't want to read any messages yet if not given DO_READING.
+ */
+
+ poll_fd.fd = _dbus_socket_get_pollable (socket_transport->fd);
+ poll_fd.events = 0;
+
+ if (_dbus_transport_try_to_authenticate (transport))
+ {
+ /* This is kind of a hack; if we have stuff to write, then try
+ * to avoid the poll. This is probably about a 5% speedup on an
+ * echo client/server.
+ *
+ * If both reading and writing were requested, we want to avoid this
+ * since it could have funky effects:
+ * - both ends spinning waiting for the other one to read
+ * data so they can finish writing
+ * - prioritizing all writing ahead of reading
+ */
+ if ((flags & DBUS_ITERATION_DO_WRITING) &&
+ !(flags & (DBUS_ITERATION_DO_READING | DBUS_ITERATION_BLOCK)) &&
+ !transport->disconnected &&
+ _dbus_connection_has_messages_to_send_unlocked (transport->connection))
+ {
+ do_writing (transport);
+
+ if (transport->disconnected ||
+ !_dbus_connection_has_messages_to_send_unlocked (transport->connection))
+ goto out;
+ }
+
+ /* If we get here, we decided to do the poll() after all */
+ _dbus_assert (socket_transport->read_watch);
+ if (flags & DBUS_ITERATION_DO_READING)
+ poll_fd.events |= _DBUS_POLLIN;
+
+ _dbus_assert (socket_transport->write_watch);
+ if (flags & DBUS_ITERATION_DO_WRITING)
+ poll_fd.events |= _DBUS_POLLOUT;
+ }
+ else
+ {
+ DBusAuthState auth_state;
+
+ auth_state = _dbus_auth_do_work (transport->auth);
+
+ if (transport->receive_credentials_pending ||
+ auth_state == DBUS_AUTH_STATE_WAITING_FOR_INPUT)
+ poll_fd.events |= _DBUS_POLLIN;
+
+ if (transport->send_credentials_pending ||
+ auth_state == DBUS_AUTH_STATE_HAVE_BYTES_TO_SEND)
+ poll_fd.events |= _DBUS_POLLOUT;
+ }
+
+ if (poll_fd.events)
+ {
+ int saved_errno;
+
+ if (flags & DBUS_ITERATION_BLOCK)
+ poll_timeout = timeout_milliseconds;
+ else
+ poll_timeout = 0;
+
+ /* For blocking selects we drop the connection lock here
+ * to avoid blocking out connection access during a potentially
+ * indefinite blocking call. The io path is still protected
+ * by the io_path_cond condvar, so we won't reenter this.
+ */
+ if (flags & DBUS_ITERATION_BLOCK)
+ {
+ _dbus_verbose ("unlock pre poll\n");
+ _dbus_connection_unlock (transport->connection);
+ }
+
+ again:
+ poll_res = _dbus_poll (&poll_fd, 1, poll_timeout);
+ saved_errno = _dbus_save_socket_errno ();
+
+ if (poll_res < 0 && _dbus_get_is_errno_eintr (saved_errno))
+ goto again;
+
+ if (flags & DBUS_ITERATION_BLOCK)
+ {
+ _dbus_verbose ("lock post poll\n");
+ _dbus_connection_lock (transport->connection);
+ }
+
+ if (poll_res >= 0)
+ {
+ if (poll_res == 0)
+ poll_fd.revents = 0; /* some concern that posix does not guarantee this;
+ * valgrind flags it as an error. though it probably
+ * is guaranteed on linux at least.
+ */
+
+ if (poll_fd.revents & _DBUS_POLLERR)
+ do_io_error (transport);
+ else
+ {
+ dbus_bool_t need_read = (poll_fd.revents & _DBUS_POLLIN) > 0;
+ dbus_bool_t need_write = (poll_fd.revents & _DBUS_POLLOUT) > 0;
+ dbus_bool_t authentication_completed;
+
+ _dbus_verbose ("in iteration, need_read=%d need_write=%d\n",
+ need_read, need_write);
+ do_authentication (transport, need_read, need_write,
+ &authentication_completed);
+
+ /* See comment in socket_handle_watch. */
+ if (authentication_completed)
+ goto out;
+
+ if (need_read && (flags & DBUS_ITERATION_DO_READING))
+ do_reading (transport);
+ if (need_write && (flags & DBUS_ITERATION_DO_WRITING))
+ do_writing (transport);
+ }
+ }
+ else
+ {
+ _dbus_verbose ("Error from _dbus_poll(): %s\n",
+ _dbus_strerror (saved_errno));
+ }
+ }
+
+
+ out:
+ /* We need to install the write watch only if we did not
+ * successfully write everything. Note we need to be careful that we
+ * don't call check_write_watch *before* do_writing, since it's
+ * inefficient to add the write watch, and we can avoid it most of
+ * the time since we can write immediately.
+ *
+ * However, we MUST always call check_write_watch(); DBusConnection code
+ * relies on the fact that running an iteration will notice that
+ * messages are pending.
+ */
+ check_write_watch (transport);
+
+ _dbus_verbose (" ... leaving do_iteration()\n");
+}
+
+static void
+socket_live_messages_changed (DBusTransport *transport)
+{
+ /* See if we should look for incoming messages again */
+ check_read_watch (transport);
+}
+
+
+static dbus_bool_t
+socket_get_socket_fd (DBusTransport *transport,
+ DBusSocket *fd_p)
+{
+ DBusTransportSocket *socket_transport = (DBusTransportSocket*) transport;
+
+ *fd_p = socket_transport->fd;
+
+ return TRUE;
+}
+
+static const DBusTransportVTable socket_vtable = {
+ socket_finalize,
+ socket_handle_watch,
+ socket_disconnect,
+ socket_connection_set,
+ socket_do_iteration,
+ socket_live_messages_changed,
+ socket_get_socket_fd
+};
+
+/**
+ * Creates a new transport for the given socket file descriptor. The file
+ * descriptor must be nonblocking (use _dbus_set_fd_nonblocking() to
+ * make it so). This function is shared by various transports that
+ * boil down to a full duplex file descriptor.
+ *
+ * @param fd the file descriptor.
+ * @param server_guid non-#NULL if this transport is on the server side of a connection
+ * @param address the transport's address
+ * @returns the new transport, or #NULL if no memory.
+ */
+DBusTransport*
+_dbus_transport_new_for_socket (DBusSocket fd,
+ const DBusString *server_guid,
+ const DBusString *address)
+{
+ DBusTransportSocket *socket_transport;
+ DBusString invalid = _DBUS_STRING_INIT_INVALID;
+
+ socket_transport = dbus_new0 (DBusTransportSocket, 1);
+ if (socket_transport == NULL)
+ return NULL;
+
+ /* So they can be "freed" without error */
+ socket_transport->encoded_outgoing = invalid;
+ socket_transport->encoded_incoming = invalid;
+
+ if (!_dbus_string_init (&socket_transport->encoded_outgoing))
+ goto failed;
+
+ if (!_dbus_string_init (&socket_transport->encoded_incoming))
+ goto failed;
+
+ socket_transport->write_watch = _dbus_watch_new (_dbus_socket_get_pollable (fd),
+ DBUS_WATCH_WRITABLE,
+ FALSE,
+ NULL, NULL, NULL);
+ if (socket_transport->write_watch == NULL)
+ goto failed;
+
+ socket_transport->read_watch = _dbus_watch_new (_dbus_socket_get_pollable (fd),
+ DBUS_WATCH_READABLE,
+ FALSE,
+ NULL, NULL, NULL);
+ if (socket_transport->read_watch == NULL)
+ goto failed;
+
+ if (!_dbus_transport_init_base (&socket_transport->base,
+ &socket_vtable,
+ server_guid, address))
+ goto failed;
+
+#ifdef HAVE_UNIX_FD_PASSING
+ _dbus_auth_set_unix_fd_possible(socket_transport->base.auth, _dbus_socket_can_pass_unix_fd(fd));
+#endif
+
+ socket_transport->fd = fd;
+ socket_transport->message_bytes_written = 0;
+
+ /* These values should probably be tunable or something. */
+ socket_transport->max_bytes_read_per_iteration = 2048;
+ socket_transport->max_bytes_written_per_iteration = 2048;
+
+ return (DBusTransport*) socket_transport;
+
+failed:
+ if (socket_transport->read_watch != NULL)
+ {
+ _dbus_watch_invalidate (socket_transport->read_watch);
+ _dbus_watch_unref (socket_transport->read_watch);
+ }
+
+ if (socket_transport->write_watch != NULL)
+ {
+ _dbus_watch_invalidate (socket_transport->write_watch);
+ _dbus_watch_unref (socket_transport->write_watch);
+ }
+
+ _dbus_string_free (&socket_transport->encoded_incoming);
+ _dbus_string_free (&socket_transport->encoded_outgoing);
+ dbus_free (socket_transport);
+ return NULL;
+}
+
+/**
+ * Creates a new transport for the given hostname and port.
+ * If host is NULL, it will default to localhost
+ *
+ * @param host the host to connect to
+ * @param port the port to connect to
+ * @param family the address family to connect to
+ * @param noncefile path to nonce file
+ * @param error location to store reason for failure.
+ * @returns a new transport, or #NULL on failure.
+ */
+DBusTransport*
+_dbus_transport_new_for_tcp_socket (const char *host,
+ const char *port,
+ const char *family,
+ const char *noncefile,
+ DBusError *error)
+{
+ DBusSocket fd;
+ DBusTransport *transport;
+ DBusString address;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ if (!_dbus_string_init (&address))
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ return NULL;
+ }
+
+ if (host == NULL)
+ host = "localhost";
+
+ if (!_dbus_string_append (&address, noncefile ? "nonce-tcp:" : "tcp:"))
+ goto error;
+
+ if (!_dbus_string_append (&address, "host=") ||
+ !_dbus_string_append (&address, host))
+ goto error;
+
+ if (!_dbus_string_append (&address, ",port=") ||
+ !_dbus_string_append (&address, port))
+ goto error;
+
+ if (family != NULL &&
+ (!_dbus_string_append (&address, ",family=") ||
+ !_dbus_string_append (&address, family)))
+ goto error;
+
+ if (noncefile != NULL &&
+ (!_dbus_string_append (&address, ",noncefile=") ||
+ !_dbus_string_append (&address, noncefile)))
+ goto error;
+
+ fd = _dbus_connect_tcp_socket_with_nonce (host, port, family, noncefile, error);
+ if (!_dbus_socket_is_valid (fd))
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ _dbus_string_free (&address);
+ return NULL;
+ }
+
+ _dbus_verbose ("Successfully connected to tcp socket %s:%s\n",
+ host, port);
+
+ transport = _dbus_transport_new_for_socket (fd, NULL, &address);
+ _dbus_string_free (&address);
+ if (transport == NULL)
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ _dbus_close_socket (&fd, NULL);
+ }
+
+ return transport;
+
+error:
+ _dbus_string_free (&address);
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ return NULL;
+}
+
+/**
+ * Opens a TCP socket transport.
+ *
+ * @param entry the address entry to try opening as a tcp transport.
+ * @param transport_p return location for the opened transport
+ * @param error error to be set
+ * @returns result of the attempt
+ */
+DBusTransportOpenResult
+_dbus_transport_open_socket(DBusAddressEntry *entry,
+ DBusTransport **transport_p,
+ DBusError *error)
+{
+ const char *method;
+ dbus_bool_t isTcp;
+ dbus_bool_t isNonceTcp;
+
+ method = dbus_address_entry_get_method (entry);
+ _dbus_assert (method != NULL);
+
+ isTcp = strcmp (method, "tcp") == 0;
+ isNonceTcp = strcmp (method, "nonce-tcp") == 0;
+
+ if (isTcp || isNonceTcp)
+ {
+ const char *host = dbus_address_entry_get_value (entry, "host");
+ const char *port = dbus_address_entry_get_value (entry, "port");
+ const char *family = dbus_address_entry_get_value (entry, "family");
+ const char *noncefile = dbus_address_entry_get_value (entry, "noncefile");
+
+ if ((isNonceTcp == TRUE) != (noncefile != NULL)) {
+ _dbus_set_bad_address (error, method, "noncefile", NULL);
+ return DBUS_TRANSPORT_OPEN_BAD_ADDRESS;
+ }
+
+ if (port == NULL)
+ {
+ _dbus_set_bad_address (error, method, "port", NULL);
+ return DBUS_TRANSPORT_OPEN_BAD_ADDRESS;
+ }
+
+ *transport_p = _dbus_transport_new_for_tcp_socket (host, port, family, noncefile, error);
+ if (*transport_p == NULL)
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ return DBUS_TRANSPORT_OPEN_DID_NOT_CONNECT;
+ }
+ else
+ {
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+ return DBUS_TRANSPORT_OPEN_OK;
+ }
+ }
+ else
+ {
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+ return DBUS_TRANSPORT_OPEN_NOT_HANDLED;
+ }
+}
+
+/**
+ * Creates a new transport for the given Unix domain socket
+ * path. This creates a client-side of a transport.
+ *
+ * @param path the path to the domain socket.
+ * @param abstract #TRUE to use abstract socket namespace
+ * @param error address where an error can be returned.
+ * @returns a new transport, or #NULL on failure.
+ */
+DBusTransport*
+_dbus_transport_new_for_domain_socket (const char *path,
+ dbus_bool_t abstract,
+ DBusError *error)
+{
+ DBusSocket fd = DBUS_SOCKET_INIT;
+ DBusTransport *transport;
+ DBusString address;
+ DBusString unescaped_path;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ if (!_dbus_string_init (&address))
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ return NULL;
+ }
+
+ _dbus_string_init_const (&unescaped_path, path);
+
+ if ((abstract &&
+ !_dbus_string_append (&address, "unix:abstract=")) ||
+ (!abstract &&
+ !_dbus_string_append (&address, "unix:path=")) ||
+ !_dbus_address_append_escaped (&address, &unescaped_path))
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ goto failed_0;
+ }
+
+ fd = _dbus_connect_unix_socket (path, abstract, error);
+ if (!_dbus_socket_is_valid (fd))
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ goto failed_0;
+ }
+
+ _dbus_verbose ("Successfully connected to unix socket %s\n",
+ path);
+
+ transport = _dbus_transport_new_for_socket (fd, NULL, &address);
+ if (transport == NULL)
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ goto failed_1;
+ }
+
+ _dbus_string_free (&address);
+
+ return transport;
+
+ failed_1:
+ _dbus_close_socket (&fd, NULL);
+ failed_0:
+ _dbus_string_free (&address);
+ return NULL;
+}
+
+/**
+ * Opens a UNIX socket transport.
+ *
+ * @param entry the address entry to try opening as a unix transport.
+ * @param transport_p return location for the opened transport
+ * @param error error to be set
+ * @returns result of the attempt
+ */
+DBusTransportOpenResult
+_dbus_transport_open_unix_socket (DBusAddressEntry *entry,
+ DBusTransport **transport_p,
+ DBusError *error)
+{
+ const char *method;
+
+ method = dbus_address_entry_get_method (entry);
+ _dbus_assert (method != NULL);
+
+ if (strcmp (method, "unix") == 0)
+ {
+ const char *path = dbus_address_entry_get_value (entry, "path");
+ const char *tmpdir = dbus_address_entry_get_value (entry, "tmpdir");
+ const char *abstract = dbus_address_entry_get_value (entry, "abstract");
+
+ if (tmpdir != NULL)
+ {
+ _dbus_set_bad_address (error, NULL, NULL,
+ "cannot use the \"tmpdir\" option for an address to connect to, only in an address to listen on");
+ return DBUS_TRANSPORT_OPEN_BAD_ADDRESS;
+ }
+
+ if (path == NULL && abstract == NULL)
+ {
+ _dbus_set_bad_address (error, "unix",
+ "path or abstract",
+ NULL);
+ return DBUS_TRANSPORT_OPEN_BAD_ADDRESS;
+ }
+
+ if (path != NULL && abstract != NULL)
+ {
+ _dbus_set_bad_address (error, NULL, NULL,
+ "can't specify both \"path\" and \"abstract\" options in an address");
+ return DBUS_TRANSPORT_OPEN_BAD_ADDRESS;
+ }
+
+ if (path)
+ *transport_p = _dbus_transport_new_for_domain_socket (path, FALSE,
+ error);
+ else
+ *transport_p = _dbus_transport_new_for_domain_socket (abstract, TRUE,
+ error);
+ if (*transport_p == NULL)
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ return DBUS_TRANSPORT_OPEN_DID_NOT_CONNECT;
+ }
+ else
+ {
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+ return DBUS_TRANSPORT_OPEN_OK;
+ }
+ }
+ else
+ {
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+ return DBUS_TRANSPORT_OPEN_NOT_HANDLED;
+ }
+}
+
+/** @} */
diff --git a/src/3rdparty/libdbus/dbus/dbus-transport-socket.h b/src/3rdparty/libdbus/dbus/dbus-transport-socket.h
new file mode 100644
index 00000000..65de3c15
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-transport-socket.h
@@ -0,0 +1,54 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-transport-socket.h Socket subclasses of DBusTransport
+ *
+ * Copyright (C) 2002, 2006 Red Hat Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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
+ *
+ */
+#ifndef DBUS_TRANSPORT_SOCKET_H
+#define DBUS_TRANSPORT_SOCKET_H
+
+#include <dbus/dbus-transport-protected.h>
+
+DBUS_BEGIN_DECLS
+
+DBusTransport* _dbus_transport_new_for_socket (DBusSocket fd,
+ const DBusString *server_guid,
+ const DBusString *address);
+DBusTransport* _dbus_transport_new_for_tcp_socket (const char *host,
+ const char *port,
+ const char *family,
+ const char *noncefile,
+ DBusError *error);
+DBusTransportOpenResult _dbus_transport_open_socket (DBusAddressEntry *entry,
+ DBusTransport **transport_p,
+ DBusError *error);
+
+DBusTransport* _dbus_transport_new_for_domain_socket (const char *path,
+ dbus_bool_t abstract,
+ DBusError *error);
+
+DBusTransportOpenResult _dbus_transport_open_unix_socket (DBusAddressEntry *entry,
+ DBusTransport **transport_p,
+ DBusError *error);
+
+DBUS_END_DECLS
+
+#endif /* DBUS_TRANSPORT_SOCKET_H */
diff --git a/src/3rdparty/libdbus/dbus/dbus-transport-unix.c b/src/3rdparty/libdbus/dbus/dbus-transport-unix.c
new file mode 100644
index 00000000..a8dfaa52
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-transport-unix.c
@@ -0,0 +1,317 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-transport-unix.c UNIX socket subclasses of DBusTransport
+ *
+ * Copyright (C) 2002, 2003, 2004 Red Hat Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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 <stdio.h>
+
+#include "dbus-internals.h"
+#include "dbus-connection-internal.h"
+#include "dbus-transport-unix.h"
+#include "dbus-transport-socket.h"
+#include "dbus-transport-protected.h"
+#include "dbus-watch.h"
+#include "dbus-sysdeps-unix.h"
+#include "dbus-test.h"
+
+/**
+ * @defgroup DBusTransportUnix DBusTransport implementations for UNIX
+ * @ingroup DBusInternals
+ * @brief Implementation details of DBusTransport on UNIX
+ *
+ * @{
+ */
+
+/**
+ * Creates a new transport for the given binary and arguments. This
+ * creates a client-side of a transport. The process will be forked
+ * off and executed with stdin/stdout connected to a local AF_UNIX
+ * socket.
+ *
+ * @param path the path to the domain socket.
+ * @param argv Parameters list
+ * @param error address where an error can be returned.
+ * @returns a new transport, or #NULL on failure.
+ */
+static DBusTransport*
+_dbus_transport_new_for_exec (const char *path,
+ char *const argv[],
+ DBusError *error)
+{
+ DBusSocket fd = DBUS_SOCKET_INIT;
+ DBusTransport *transport;
+ DBusString address;
+ unsigned i;
+ char *escaped;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ if (!_dbus_string_init (&address))
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ return NULL;
+ }
+
+ escaped = dbus_address_escape_value (path);
+ if (!escaped)
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ goto failed;
+ }
+
+ if (!_dbus_string_append (&address, "unixexec:path=") ||
+ !_dbus_string_append (&address, escaped))
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ dbus_free (escaped);
+ goto failed;
+ }
+
+ dbus_free (escaped);
+
+ if (argv)
+ {
+ for (i = 0; argv[i]; i++)
+ {
+ dbus_bool_t success;
+
+ escaped = dbus_address_escape_value (argv[i]);
+ if (!escaped)
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ goto failed;
+ }
+
+ success = _dbus_string_append_printf (&address, ",argv%u=%s", i, escaped);
+ dbus_free (escaped);
+
+ if (!success)
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ goto failed;
+ }
+ }
+ }
+
+ fd = _dbus_connect_exec (path, argv, error);
+ if (!_dbus_socket_is_valid (fd))
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ goto failed;
+ }
+
+ _dbus_verbose ("Successfully connected to process %s\n",
+ path);
+
+ transport = _dbus_transport_new_for_socket (fd, NULL, &address);
+ if (transport == NULL)
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ goto failed;
+ }
+
+ _dbus_string_free (&address);
+
+ return transport;
+
+ failed:
+ if (_dbus_socket_is_valid (fd))
+ _dbus_close_socket (&fd, NULL);
+
+ _dbus_string_free (&address);
+ return NULL;
+}
+
+
+DBusTransportOpenResult
+_dbus_transport_open_unixexec (DBusAddressEntry *entry,
+ DBusTransport **transport_p,
+ DBusError *error)
+{
+ const char *method;
+
+ method = dbus_address_entry_get_method (entry);
+ _dbus_assert (method != NULL);
+
+ if (strcmp (method, "unixexec") == 0)
+ {
+ const char *path;
+ unsigned i;
+ char **argv;
+
+ path = dbus_address_entry_get_value (entry, "path");
+ if (path == NULL)
+ {
+ _dbus_set_bad_address (error, NULL, NULL,
+ "No process path specified");
+ return DBUS_TRANSPORT_OPEN_BAD_ADDRESS;
+ }
+
+ /* First count argv arguments */
+ for (i = 1; ; i++)
+ {
+ char t[4+20+1]; /* "argv" plus space for a formatted base 10 64bit integer, plus NUL */
+
+ snprintf (t, sizeof(t), "argv%u", i);
+
+ if (!dbus_address_entry_get_value (entry, t))
+ break;
+ }
+
+ /* Allocate string array */
+ argv = dbus_new0 (char*, i+1);
+ if (!argv)
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ return DBUS_TRANSPORT_OPEN_DID_NOT_CONNECT;
+ }
+
+ /* Fill in string array */
+ for (i = 0; ; i++)
+ {
+ char t[4+20+1];
+ const char *p;
+
+ snprintf (t, sizeof(t), "argv%u", i);
+
+ p = dbus_address_entry_get_value (entry, t);
+ if (!p)
+ {
+ if (i == 0)
+ /* If argv0 isn't specified, fill in the path instead */
+ p = path;
+ else
+ break;
+ }
+
+ argv[i] = _dbus_strdup (p);
+ if (!argv[i])
+ {
+ dbus_free_string_array (argv);
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ return DBUS_TRANSPORT_OPEN_DID_NOT_CONNECT;
+ }
+ }
+
+ *transport_p = _dbus_transport_new_for_exec (path, argv, error);
+ dbus_free_string_array (argv);
+
+ if (*transport_p == NULL)
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ return DBUS_TRANSPORT_OPEN_DID_NOT_CONNECT;
+ }
+ else
+ {
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+ return DBUS_TRANSPORT_OPEN_OK;
+ }
+ }
+ else
+ {
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+ return DBUS_TRANSPORT_OPEN_NOT_HANDLED;
+ }
+}
+
+/**
+ * Opens platform specific transport types.
+ *
+ * @param entry the address entry to try opening
+ * @param transport_p return location for the opened transport
+ * @param error error to be set
+ * @returns result of the attempt
+ */
+DBusTransportOpenResult
+_dbus_transport_open_platform_specific (DBusAddressEntry *entry,
+ DBusTransport **transport_p,
+ DBusError *error)
+{
+#ifdef DBUS_ENABLE_LAUNCHD
+ const char *method;
+
+ method = dbus_address_entry_get_method (entry);
+ _dbus_assert (method != NULL);
+
+ if (strcmp (method, "launchd") == 0)
+ {
+ DBusError tmp_error = DBUS_ERROR_INIT;
+ const char *launchd_env_var = dbus_address_entry_get_value (entry, "env");
+ const char *launchd_socket;
+ DBusString socket_path;
+ dbus_bool_t valid_socket;
+
+ if (!_dbus_string_init (&socket_path))
+ {
+ _DBUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ if (launchd_env_var == NULL)
+ {
+ _dbus_set_bad_address (error, "launchd", "env", NULL);
+ return DBUS_TRANSPORT_OPEN_BAD_ADDRESS;
+ }
+
+ valid_socket = _dbus_lookup_launchd_socket (&socket_path, launchd_env_var, error);
+
+ if (dbus_error_is_set(error))
+ {
+ _dbus_string_free(&socket_path);
+ return DBUS_TRANSPORT_OPEN_DID_NOT_CONNECT;
+ }
+
+ if (!valid_socket)
+ {
+ dbus_set_error(&tmp_error, DBUS_ERROR_BAD_ADDRESS,
+ "launchd's env var %s does not exist", launchd_env_var);
+ dbus_error_free(error);
+ dbus_move_error(&tmp_error, error);
+ return DBUS_TRANSPORT_OPEN_DID_NOT_CONNECT;
+ }
+
+ launchd_socket = _dbus_string_get_const_data(&socket_path);
+ *transport_p = _dbus_transport_new_for_domain_socket (launchd_socket, FALSE, error);
+
+ if (*transport_p == NULL)
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ return DBUS_TRANSPORT_OPEN_DID_NOT_CONNECT;
+ }
+ else
+ {
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+ return DBUS_TRANSPORT_OPEN_OK;
+ }
+ }
+ else
+#endif /* DBUS_ENABLE_LAUNCHD */
+ {
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+ return DBUS_TRANSPORT_OPEN_NOT_HANDLED;
+ }
+}
+
+/** @} */
diff --git a/src/3rdparty/libdbus/dbus/dbus-transport-unix.h b/src/3rdparty/libdbus/dbus/dbus-transport-unix.h
new file mode 100644
index 00000000..df30a179
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-transport-unix.h
@@ -0,0 +1,37 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-transport-unix.h UNIX socket subclasses of DBusTransport
+ *
+ * Copyright (C) 2002 Red Hat Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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
+ *
+ */
+#ifndef DBUS_TRANSPORT_UNIX_H
+#define DBUS_TRANSPORT_UNIX_H
+
+#include <dbus/dbus-transport-protected.h>
+
+DBUS_BEGIN_DECLS
+
+DBusTransportOpenResult _dbus_transport_open_unixexec (DBusAddressEntry *entry,
+ DBusTransport **transport_p,
+ DBusError *error);
+DBUS_END_DECLS
+
+#endif /* DBUS_TRANSPORT_UNIX_H */
diff --git a/src/3rdparty/libdbus/dbus/dbus-transport-win.c b/src/3rdparty/libdbus/dbus/dbus-transport-win.c
new file mode 100644
index 00000000..6a0dddce
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-transport-win.c
@@ -0,0 +1,60 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-transport-win.c Windows socket subclasses of DBusTransport
+ *
+ * Copyright (C) 2002, 2003, 2004 Red Hat Inc.
+ * Copyright (C) 2007 Ralf Habacker <ralf.habacker@freenet.de>
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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-connection-internal.h"
+#include "dbus-transport-socket.h"
+#include "dbus-transport-protected.h"
+#include "dbus-watch.h"
+#include "dbus-sysdeps-win.h"
+
+/**
+ * @defgroup DBusTransportUnix DBusTransport implementations for UNIX
+ * @ingroup DBusInternals
+ * @brief Implementation details of DBusTransport on UNIX
+ *
+ * @{
+ */
+
+/**
+ * Opens platform specific transport types.
+ *
+ * @param entry the address entry to try opening
+ * @param transport_p return location for the opened transport
+ * @param error error to be set
+ * @returns result of the attempt
+ */
+DBusTransportOpenResult
+_dbus_transport_open_platform_specific (DBusAddressEntry *entry,
+ DBusTransport **transport_p,
+ DBusError *error)
+{
+ /* currently no Windows-specific transports */
+ return DBUS_TRANSPORT_OPEN_NOT_HANDLED;
+}
+
+/** @} */
diff --git a/src/3rdparty/libdbus/dbus/dbus-transport-win.h b/src/3rdparty/libdbus/dbus/dbus-transport-win.h
new file mode 100644
index 00000000..28ccfa85
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-transport-win.h
@@ -0,0 +1,35 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-transport-win.h Windows socket subclasses of DBusTransport
+ *
+ * Copyright (C) 2002 Red Hat Inc.
+ * Copyright (C) 2007 Ralf Habacker <ralf.habacker@freenet.de>
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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
+ *
+ */
+#ifndef DBUS_TRANSPORT_WIN_H
+#define DBUS_TRANSPORT_WIN_H
+
+#include <dbus/dbus-transport.h>
+
+DBUS_BEGIN_DECLS
+
+DBUS_END_DECLS
+
+#endif /* DBUS_TRANSPORT_WIN_H */
diff --git a/src/3rdparty/libdbus/dbus/dbus-transport.c b/src/3rdparty/libdbus/dbus/dbus-transport.c
new file mode 100644
index 00000000..f6119754
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-transport.c
@@ -0,0 +1,1633 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-transport.c DBusTransport object (internal to D-Bus implementation)
+ *
+ * Copyright (C) 2002, 2003 Red Hat Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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-transport-protected.h"
+#include "dbus-transport-unix.h"
+#include "dbus-transport-socket.h"
+#include "dbus-connection-internal.h"
+#include "dbus-watch.h"
+#include "dbus-auth.h"
+#include "dbus-address.h"
+#include "dbus-credentials.h"
+//#include "dbus-mainloop.h"
+#include "dbus-message.h"
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
+#include "dbus-server-debug-pipe.h"
+#endif
+
+/**
+ * @defgroup DBusTransport DBusTransport object
+ * @ingroup DBusInternals
+ * @brief "Backend" for a DBusConnection.
+ *
+ * Types and functions related to DBusTransport. A transport is an
+ * abstraction that can send and receive data via various kinds of
+ * network connections or other IPC mechanisms.
+ *
+ * @{
+ */
+
+/**
+ * @typedef DBusTransport
+ *
+ * Opaque object representing a way message stream.
+ * DBusTransport abstracts various kinds of actual
+ * transport mechanism, such as different network protocols,
+ * or encryption schemes.
+ */
+
+static void
+live_messages_notify (DBusCounter *counter,
+ void *user_data)
+{
+ DBusTransport *transport = user_data;
+
+ _dbus_connection_lock (transport->connection);
+ _dbus_transport_ref (transport);
+
+#if 0
+ _dbus_verbose ("Size counter value is now %d\n",
+ (int) _dbus_counter_get_size_value (counter));
+ _dbus_verbose ("Unix FD counter value is now %d\n",
+ (int) _dbus_counter_get_unix_fd_value (counter));
+#endif
+
+ /* disable or re-enable the read watch for the transport if
+ * required.
+ */
+ if (transport->vtable->live_messages_changed)
+ {
+ (* transport->vtable->live_messages_changed) (transport);
+ }
+
+ _dbus_transport_unref (transport);
+ _dbus_connection_unlock (transport->connection);
+}
+
+/**
+ * Initializes the base class members of DBusTransport. Chained up to
+ * by subclasses in their constructor. The server GUID is the
+ * globally unique ID for the server creating this connection
+ * and will be #NULL for the client side of a connection. The GUID
+ * is in hex format.
+ *
+ * @param transport the transport being created.
+ * @param vtable the subclass vtable.
+ * @param server_guid non-#NULL if this transport is on the server side of a connection
+ * @param address the address of the transport
+ * @returns #TRUE on success.
+ */
+dbus_bool_t
+_dbus_transport_init_base (DBusTransport *transport,
+ const DBusTransportVTable *vtable,
+ const DBusString *server_guid,
+ const DBusString *address)
+{
+ DBusMessageLoader *loader;
+ DBusAuth *auth;
+ DBusCounter *counter;
+ char *address_copy;
+ DBusCredentials *creds;
+
+ loader = _dbus_message_loader_new ();
+ if (loader == NULL)
+ return FALSE;
+
+ if (server_guid)
+ auth = _dbus_auth_server_new (server_guid);
+ else
+ auth = _dbus_auth_client_new ();
+ if (auth == NULL)
+ {
+ _dbus_message_loader_unref (loader);
+ return FALSE;
+ }
+
+ counter = _dbus_counter_new ();
+ if (counter == NULL)
+ {
+ _dbus_auth_unref (auth);
+ _dbus_message_loader_unref (loader);
+ return FALSE;
+ }
+
+ creds = _dbus_credentials_new ();
+ if (creds == NULL)
+ {
+ _dbus_counter_unref (counter);
+ _dbus_auth_unref (auth);
+ _dbus_message_loader_unref (loader);
+ return FALSE;
+ }
+
+ if (server_guid)
+ {
+ _dbus_assert (address == NULL);
+ address_copy = NULL;
+ }
+ else
+ {
+ _dbus_assert (address != NULL);
+
+ if (!_dbus_string_copy_data (address, &address_copy))
+ {
+ _dbus_credentials_unref (creds);
+ _dbus_counter_unref (counter);
+ _dbus_auth_unref (auth);
+ _dbus_message_loader_unref (loader);
+ return FALSE;
+ }
+ }
+
+ transport->refcount = 1;
+ transport->vtable = vtable;
+ transport->loader = loader;
+ transport->auth = auth;
+ transport->live_messages = counter;
+ transport->authenticated = FALSE;
+ transport->disconnected = FALSE;
+ transport->is_server = (server_guid != NULL);
+ transport->send_credentials_pending = !transport->is_server;
+ transport->receive_credentials_pending = transport->is_server;
+ transport->address = address_copy;
+
+ transport->unix_user_function = NULL;
+ transport->unix_user_data = NULL;
+ transport->free_unix_user_data = NULL;
+
+ transport->windows_user_function = NULL;
+ transport->windows_user_data = NULL;
+ transport->free_windows_user_data = NULL;
+
+ transport->expected_guid = NULL;
+
+ /* Try to default to something that won't totally hose the system,
+ * but doesn't impose too much of a limitation.
+ */
+ transport->max_live_messages_size = _DBUS_ONE_MEGABYTE * 63;
+
+ /* On Linux RLIMIT_NOFILE defaults to 1024, so allowing 4096 fds live
+ should be more than enough */
+ transport->max_live_messages_unix_fds = 4096;
+
+ /* credentials read from socket if any */
+ transport->credentials = creds;
+
+ _dbus_counter_set_notify (transport->live_messages,
+ transport->max_live_messages_size,
+ transport->max_live_messages_unix_fds,
+ live_messages_notify,
+ transport);
+
+ if (transport->address)
+ _dbus_verbose ("Initialized transport on address %s\n", transport->address);
+
+ return TRUE;
+}
+
+/**
+ * Finalizes base class members of DBusTransport.
+ * Chained up to from subclass finalizers.
+ *
+ * @param transport the transport.
+ */
+void
+_dbus_transport_finalize_base (DBusTransport *transport)
+{
+ if (!transport->disconnected)
+ _dbus_transport_disconnect (transport);
+
+ if (transport->free_unix_user_data != NULL)
+ (* transport->free_unix_user_data) (transport->unix_user_data);
+
+ if (transport->free_windows_user_data != NULL)
+ (* transport->free_windows_user_data) (transport->windows_user_data);
+
+ _dbus_message_loader_unref (transport->loader);
+ _dbus_auth_unref (transport->auth);
+ _dbus_counter_set_notify (transport->live_messages,
+ 0, 0, NULL, NULL);
+ _dbus_counter_unref (transport->live_messages);
+ dbus_free (transport->address);
+ dbus_free (transport->expected_guid);
+ if (transport->credentials)
+ _dbus_credentials_unref (transport->credentials);
+}
+
+
+/**
+ * Verifies if a given D-Bus address is a valid address
+ * by attempting to connect to it. If it is, returns the
+ * opened DBusTransport object. If it isn't, returns #NULL
+ * and sets @p error.
+ *
+ * @param address the address to be checked.
+ * @param error address where an error can be returned.
+ * @returns a new transport, or #NULL on failure.
+ */
+static DBusTransport*
+check_address (const char *address, DBusError *error)
+{
+ DBusAddressEntry **entries;
+ DBusTransport *transport = NULL;
+ int len, i;
+
+ _dbus_assert (address != NULL);
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ if (!dbus_parse_address (address, &entries, &len, error))
+ return NULL; /* not a valid address */
+
+ for (i = 0; i < len; i++)
+ {
+ dbus_error_free (error);
+ transport = _dbus_transport_open (entries[i], error);
+
+ if (transport != NULL)
+ break;
+ }
+
+ dbus_address_entries_free (entries);
+ return transport;
+}
+
+/**
+ * Creates a new transport for the "autostart" method.
+ * This creates a client-side of a transport.
+ *
+ * @param scope scope of autolaunch (Windows only)
+ * @param error address where an error can be returned.
+ * @returns a new transport, or #NULL on failure.
+ */
+static DBusTransport*
+_dbus_transport_new_for_autolaunch (const char *scope, DBusError *error)
+{
+ DBusString address;
+ DBusTransport *result = NULL;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ if (!_dbus_string_init (&address))
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ return NULL;
+ }
+
+ if (!_dbus_get_autolaunch_address (scope, &address, error))
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ goto out;
+ }
+
+ result = check_address (_dbus_string_get_const_data (&address), error);
+ _DBUS_ASSERT_ERROR_XOR_BOOL (error, result != NULL);
+
+ out:
+ _dbus_string_free (&address);
+ return result;
+}
+
+static DBusTransportOpenResult
+_dbus_transport_open_autolaunch (DBusAddressEntry *entry,
+ DBusTransport **transport_p,
+ DBusError *error)
+{
+ const char *method;
+
+ method = dbus_address_entry_get_method (entry);
+ _dbus_assert (method != NULL);
+
+ if (strcmp (method, "autolaunch") == 0)
+ {
+ const char *scope = dbus_address_entry_get_value (entry, "scope");
+
+ *transport_p = _dbus_transport_new_for_autolaunch (scope, error);
+
+ if (*transport_p == NULL)
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ return DBUS_TRANSPORT_OPEN_DID_NOT_CONNECT;
+ }
+ else
+ {
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+ return DBUS_TRANSPORT_OPEN_OK;
+ }
+ }
+ else
+ {
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+ return DBUS_TRANSPORT_OPEN_NOT_HANDLED;
+ }
+}
+
+static const struct {
+ DBusTransportOpenResult (* func) (DBusAddressEntry *entry,
+ DBusTransport **transport_p,
+ DBusError *error);
+} open_funcs[] = {
+ { _dbus_transport_open_socket },
+ { _dbus_transport_open_unix_socket },
+#ifndef _WIN32
+ { _dbus_transport_open_unixexec },
+#endif
+ { _dbus_transport_open_platform_specific },
+ { _dbus_transport_open_autolaunch }
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
+ , { _dbus_transport_open_debug_pipe }
+#endif
+};
+
+/**
+ * Try to open a new transport for the given address entry. (This
+ * opens a client-side-of-the-connection transport.)
+ *
+ * @param entry the address entry
+ * @param error location to store reason for failure.
+ * @returns new transport of #NULL on failure.
+ */
+DBusTransport*
+_dbus_transport_open (DBusAddressEntry *entry,
+ DBusError *error)
+{
+ DBusTransport *transport;
+ const char *expected_guid_orig;
+ char *expected_guid;
+ int i;
+ DBusError tmp_error = DBUS_ERROR_INIT;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ transport = NULL;
+ expected_guid_orig = dbus_address_entry_get_value (entry, "guid");
+ expected_guid = _dbus_strdup (expected_guid_orig);
+
+ if (expected_guid_orig != NULL && expected_guid == NULL)
+ {
+ _DBUS_SET_OOM (error);
+ return NULL;
+ }
+
+ for (i = 0; i < (int) _DBUS_N_ELEMENTS (open_funcs); ++i)
+ {
+ DBusTransportOpenResult result;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (&tmp_error);
+ result = (* open_funcs[i].func) (entry, &transport, &tmp_error);
+
+ switch (result)
+ {
+ case DBUS_TRANSPORT_OPEN_OK:
+ _DBUS_ASSERT_ERROR_IS_CLEAR (&tmp_error);
+ goto out;
+ break;
+ case DBUS_TRANSPORT_OPEN_NOT_HANDLED:
+ _DBUS_ASSERT_ERROR_IS_CLEAR (&tmp_error);
+ /* keep going through the loop of open funcs */
+ break;
+ case DBUS_TRANSPORT_OPEN_BAD_ADDRESS:
+ _DBUS_ASSERT_ERROR_IS_SET (&tmp_error);
+ goto out;
+ break;
+ case DBUS_TRANSPORT_OPEN_DID_NOT_CONNECT:
+ _DBUS_ASSERT_ERROR_IS_SET (&tmp_error);
+ goto out;
+ break;
+ default:
+ _dbus_assert_not_reached ("invalid transport open result");
+ break;
+ }
+ }
+
+ out:
+
+ if (transport == NULL)
+ {
+ if (!dbus_error_is_set (&tmp_error))
+ _dbus_set_bad_address (&tmp_error,
+ NULL, NULL,
+ "Unknown address type (examples of valid types are \"tcp\" and on UNIX \"unix\")");
+
+ _DBUS_ASSERT_ERROR_IS_SET (&tmp_error);
+ dbus_move_error(&tmp_error, error);
+ dbus_free (expected_guid);
+ }
+ else
+ {
+ _DBUS_ASSERT_ERROR_IS_CLEAR (&tmp_error);
+
+ /* In the case of autostart the initial guid is NULL
+ * and the autostart transport recursively calls
+ * _dbus_open_transport wich returns a transport
+ * with a guid. That guid is the definitive one.
+ *
+ * FIXME: if more transports are added they may have
+ * an effect on the expected_guid semantics (i.e.
+ * expected_guid and transport->expected_guid may
+ * both have values). This is very unlikely though
+ * we should either throw asserts here for those
+ * corner cases or refactor the code so it is
+ * clearer on what is expected and what is not
+ */
+ if(expected_guid)
+ transport->expected_guid = expected_guid;
+ }
+
+ return transport;
+}
+
+/**
+ * Increments the reference count for the transport.
+ *
+ * @param transport the transport.
+ * @returns the transport.
+ */
+DBusTransport *
+_dbus_transport_ref (DBusTransport *transport)
+{
+ _dbus_assert (transport->refcount > 0);
+
+ transport->refcount += 1;
+
+ return transport;
+}
+
+/**
+ * Decrements the reference count for the transport.
+ * Disconnects and finalizes the transport if
+ * the reference count reaches zero.
+ *
+ * @param transport the transport.
+ */
+void
+_dbus_transport_unref (DBusTransport *transport)
+{
+ _dbus_assert (transport != NULL);
+ _dbus_assert (transport->refcount > 0);
+
+ transport->refcount -= 1;
+ if (transport->refcount == 0)
+ {
+ _dbus_verbose ("finalizing\n");
+
+ _dbus_assert (transport->vtable->finalize != NULL);
+
+ (* transport->vtable->finalize) (transport);
+ }
+}
+
+/**
+ * Closes our end of the connection to a remote application. Further
+ * attempts to use this transport will fail. Only the first call to
+ * _dbus_transport_disconnect() will have an effect.
+ *
+ * @param transport the transport.
+ *
+ */
+void
+_dbus_transport_disconnect (DBusTransport *transport)
+{
+ _dbus_verbose ("start\n");
+
+ _dbus_assert (transport->vtable->disconnect != NULL);
+
+ if (transport->disconnected)
+ return;
+
+ (* transport->vtable->disconnect) (transport);
+
+ transport->disconnected = TRUE;
+
+ _dbus_verbose ("end\n");
+}
+
+/**
+ * Returns #TRUE if the transport has not been disconnected.
+ * Disconnection can result from _dbus_transport_disconnect()
+ * or because the server drops its end of the connection.
+ *
+ * @param transport the transport.
+ * @returns whether we're connected
+ */
+dbus_bool_t
+_dbus_transport_get_is_connected (DBusTransport *transport)
+{
+ return !transport->disconnected;
+}
+
+static dbus_bool_t
+auth_via_unix_user_function (DBusTransport *transport)
+{
+ DBusCredentials *auth_identity;
+ dbus_bool_t allow;
+ DBusConnection *connection;
+ DBusAllowUnixUserFunction unix_user_function;
+ void *unix_user_data;
+ dbus_uid_t uid;
+
+ /* Dropping the lock here probably isn't that safe. */
+
+ auth_identity = _dbus_auth_get_identity (transport->auth);
+ _dbus_assert (auth_identity != NULL);
+
+ connection = transport->connection;
+ unix_user_function = transport->unix_user_function;
+ unix_user_data = transport->unix_user_data;
+ uid = _dbus_credentials_get_unix_uid (auth_identity);
+
+ _dbus_verbose ("unlock\n");
+ _dbus_connection_unlock (connection);
+
+ allow = (* unix_user_function) (connection,
+ uid,
+ unix_user_data);
+
+ _dbus_verbose ("lock post unix user function\n");
+ _dbus_connection_lock (connection);
+
+ if (allow)
+ {
+ _dbus_verbose ("Client UID "DBUS_UID_FORMAT" authorized\n", uid);
+ }
+ else
+ {
+ _dbus_verbose ("Client UID "DBUS_UID_FORMAT
+ " was rejected, disconnecting\n",
+ _dbus_credentials_get_unix_uid (auth_identity));
+ _dbus_transport_disconnect (transport);
+ }
+
+ return allow;
+}
+
+static dbus_bool_t
+auth_via_windows_user_function (DBusTransport *transport)
+{
+ DBusCredentials *auth_identity;
+ dbus_bool_t allow;
+ DBusConnection *connection;
+ DBusAllowWindowsUserFunction windows_user_function;
+ void *windows_user_data;
+ char *windows_sid;
+
+ /* Dropping the lock here probably isn't that safe. */
+
+ auth_identity = _dbus_auth_get_identity (transport->auth);
+ _dbus_assert (auth_identity != NULL);
+
+ connection = transport->connection;
+ windows_user_function = transport->windows_user_function;
+ windows_user_data = transport->unix_user_data;
+ windows_sid = _dbus_strdup (_dbus_credentials_get_windows_sid (auth_identity));
+
+ if (windows_sid == NULL)
+ {
+ /* OOM */
+ return FALSE;
+ }
+
+ _dbus_verbose ("unlock\n");
+ _dbus_connection_unlock (connection);
+
+ allow = (* windows_user_function) (connection,
+ windows_sid,
+ windows_user_data);
+
+ _dbus_verbose ("lock post windows user function\n");
+ _dbus_connection_lock (connection);
+
+ if (allow)
+ {
+ _dbus_verbose ("Client SID '%s' authorized\n", windows_sid);
+ }
+ else
+ {
+ _dbus_verbose ("Client SID '%s' was rejected, disconnecting\n",
+ _dbus_credentials_get_windows_sid (auth_identity));
+ _dbus_transport_disconnect (transport);
+ }
+
+ return allow;
+}
+
+static dbus_bool_t
+auth_via_default_rules (DBusTransport *transport)
+{
+ DBusCredentials *auth_identity;
+ DBusCredentials *our_identity;
+ dbus_bool_t allow;
+
+ auth_identity = _dbus_auth_get_identity (transport->auth);
+ _dbus_assert (auth_identity != NULL);
+
+ /* By default, connection is allowed if the client is 1) root or 2)
+ * has the same UID as us or 3) anonymous is allowed.
+ */
+
+ our_identity = _dbus_credentials_new_from_current_process ();
+ if (our_identity == NULL)
+ {
+ /* OOM */
+ return FALSE;
+ }
+
+ if (transport->allow_anonymous ||
+ _dbus_credentials_get_unix_uid (auth_identity) == 0 ||
+ _dbus_credentials_same_user (our_identity,
+ auth_identity))
+ {
+ if (_dbus_credentials_include(our_identity,DBUS_CREDENTIAL_WINDOWS_SID))
+ _dbus_verbose ("Client authorized as SID '%s'"
+ "matching our SID '%s'\n",
+ _dbus_credentials_get_windows_sid(auth_identity),
+ _dbus_credentials_get_windows_sid(our_identity));
+ else
+ _dbus_verbose ("Client authorized as UID "DBUS_UID_FORMAT
+ " matching our UID "DBUS_UID_FORMAT"\n",
+ _dbus_credentials_get_unix_uid(auth_identity),
+ _dbus_credentials_get_unix_uid(our_identity));
+ /* We have authenticated! */
+ allow = TRUE;
+ }
+ else
+ {
+ if (_dbus_credentials_include(our_identity,DBUS_CREDENTIAL_WINDOWS_SID))
+ _dbus_verbose ("Client authorized as SID '%s'"
+ " but our SID is '%s', disconnecting\n",
+ (_dbus_credentials_get_windows_sid(auth_identity) ?
+ _dbus_credentials_get_windows_sid(auth_identity) : "<null>"),
+ (_dbus_credentials_get_windows_sid(our_identity) ?
+ _dbus_credentials_get_windows_sid(our_identity) : "<null>"));
+ else
+ _dbus_verbose ("Client authorized as UID "DBUS_UID_FORMAT
+ " but our UID is "DBUS_UID_FORMAT", disconnecting\n",
+ _dbus_credentials_get_unix_uid(auth_identity),
+ _dbus_credentials_get_unix_uid(our_identity));
+ _dbus_transport_disconnect (transport);
+ allow = FALSE;
+ }
+
+ _dbus_credentials_unref (our_identity);
+
+ return allow;
+}
+
+/**
+ * Returns #TRUE if we have been authenticated. It will return #TRUE even if
+ * the transport is now disconnected, but was ever authenticated before
+ * disconnecting.
+ *
+ * This replaces the older _dbus_transport_get_is_authenticated() which
+ * had side-effects.
+ *
+ * @param transport the transport
+ * @returns whether we're authenticated
+ */
+dbus_bool_t
+_dbus_transport_peek_is_authenticated (DBusTransport *transport)
+{
+ return transport->authenticated;
+}
+
+/**
+ * Returns #TRUE if we have been authenticated. It will return #TRUE even if
+ * the transport is now disconnected, but was ever authenticated before
+ * disconnecting.
+ *
+ * If we have not finished authenticating, but we have enough buffered input
+ * to finish the job, then this function will do so before it returns.
+ *
+ * This used to be called _dbus_transport_get_is_authenticated(), but that
+ * name seems inappropriate for a function with side-effects.
+ *
+ * @todo we drop connection->mutex when calling the unix_user_function,
+ * and windows_user_function, which may not be safe really.
+ *
+ * @param transport the transport
+ * @returns whether we're authenticated
+ */
+dbus_bool_t
+_dbus_transport_try_to_authenticate (DBusTransport *transport)
+{
+ if (transport->authenticated)
+ return TRUE;
+ else
+ {
+ dbus_bool_t maybe_authenticated;
+
+ if (transport->disconnected)
+ return FALSE;
+
+ /* paranoia ref since we call user callbacks sometimes */
+ _dbus_connection_ref_unlocked (transport->connection);
+
+ maybe_authenticated =
+ (!(transport->send_credentials_pending ||
+ transport->receive_credentials_pending));
+
+ if (maybe_authenticated)
+ {
+ switch (_dbus_auth_do_work (transport->auth))
+ {
+ case DBUS_AUTH_STATE_AUTHENTICATED:
+ /* leave as maybe_authenticated */
+ break;
+
+ case DBUS_AUTH_STATE_WAITING_FOR_INPUT:
+ case DBUS_AUTH_STATE_WAITING_FOR_MEMORY:
+ case DBUS_AUTH_STATE_HAVE_BYTES_TO_SEND:
+ case DBUS_AUTH_STATE_NEED_DISCONNECT:
+ maybe_authenticated = FALSE;
+ break;
+
+ case DBUS_AUTH_STATE_INVALID:
+ default:
+ _dbus_assert_not_reached ("invalid authentication state");
+ }
+ }
+
+ /* If we're the client, verify the GUID
+ */
+ if (maybe_authenticated && !transport->is_server)
+ {
+ const char *server_guid;
+
+ server_guid = _dbus_auth_get_guid_from_server (transport->auth);
+ _dbus_assert (server_guid != NULL);
+
+ if (transport->expected_guid &&
+ strcmp (transport->expected_guid, server_guid) != 0)
+ {
+ _dbus_verbose ("Client expected GUID '%s' and we got '%s' from the server\n",
+ transport->expected_guid, server_guid);
+ _dbus_transport_disconnect (transport);
+ _dbus_connection_unref_unlocked (transport->connection);
+ return FALSE;
+ }
+ }
+
+ /* If we're the server, see if we want to allow this identity to proceed.
+ */
+ if (maybe_authenticated && transport->is_server)
+ {
+ dbus_bool_t allow;
+ DBusCredentials *auth_identity;
+
+ auth_identity = _dbus_auth_get_identity (transport->auth);
+ _dbus_assert (auth_identity != NULL);
+
+ /* If we have an auth'd user and a user function, delegate
+ * deciding whether auth credentials are good enough to the
+ * app; otherwise, use our default decision process.
+ */
+ if (transport->unix_user_function != NULL &&
+ _dbus_credentials_include (auth_identity, DBUS_CREDENTIAL_UNIX_USER_ID))
+ {
+ allow = auth_via_unix_user_function (transport);
+ }
+ else if (transport->windows_user_function != NULL &&
+ _dbus_credentials_include (auth_identity, DBUS_CREDENTIAL_WINDOWS_SID))
+ {
+ allow = auth_via_windows_user_function (transport);
+ }
+ else
+ {
+ allow = auth_via_default_rules (transport);
+ }
+
+ if (!allow)
+ maybe_authenticated = FALSE;
+ }
+
+ transport->authenticated = maybe_authenticated;
+
+ _dbus_connection_unref_unlocked (transport->connection);
+ return maybe_authenticated;
+ }
+}
+
+/**
+ * See dbus_connection_get_is_anonymous().
+ *
+ * @param transport the transport
+ * @returns #TRUE if not authenticated or authenticated as anonymous
+ */
+dbus_bool_t
+_dbus_transport_get_is_anonymous (DBusTransport *transport)
+{
+ DBusCredentials *auth_identity;
+
+ if (!transport->authenticated)
+ return TRUE;
+
+ auth_identity = _dbus_auth_get_identity (transport->auth);
+
+ if (_dbus_credentials_are_anonymous (auth_identity))
+ return TRUE;
+ else
+ return FALSE;
+}
+
+/**
+ * Returns TRUE if the transport supports sending unix fds.
+ *
+ * @param transport the transport
+ * @returns #TRUE if TRUE it is possible to send unix fds across the transport.
+ */
+dbus_bool_t
+_dbus_transport_can_pass_unix_fd(DBusTransport *transport)
+{
+ return DBUS_TRANSPORT_CAN_SEND_UNIX_FD(transport);
+}
+
+/**
+ * Gets the address of a transport. It will be
+ * #NULL for a server-side transport.
+ *
+ * @param transport the transport
+ * @returns transport's address
+ */
+const char*
+_dbus_transport_get_address (DBusTransport *transport)
+{
+ return transport->address;
+}
+
+/**
+ * Gets the id of the server we are connected to (see
+ * dbus_server_get_id()). Only works on client side.
+ *
+ * @param transport the transport
+ * @returns transport's server's id or #NULL if we are the server side
+ */
+const char*
+_dbus_transport_get_server_id (DBusTransport *transport)
+{
+ if (transport->is_server)
+ return NULL;
+ else if (transport->authenticated)
+ return _dbus_auth_get_guid_from_server (transport->auth);
+ else
+ return transport->expected_guid;
+}
+
+/**
+ * Handles a watch by reading data, writing data, or disconnecting
+ * the transport, as appropriate for the given condition.
+ *
+ * @param transport the transport.
+ * @param watch the watch.
+ * @param condition the current state of the watched file descriptor.
+ * @returns #FALSE if not enough memory to fully handle the watch
+ */
+dbus_bool_t
+_dbus_transport_handle_watch (DBusTransport *transport,
+ DBusWatch *watch,
+ unsigned int condition)
+{
+ dbus_bool_t retval;
+
+ _dbus_assert (transport->vtable->handle_watch != NULL);
+
+ if (transport->disconnected)
+ return TRUE;
+
+ if (dbus_watch_get_socket (watch) < 0)
+ {
+ _dbus_warn_check_failed ("Tried to handle an invalidated watch; this watch should have been removed");
+ return TRUE;
+ }
+
+ _dbus_watch_sanitize_condition (watch, &condition);
+
+ _dbus_transport_ref (transport);
+ _dbus_watch_ref (watch);
+ retval = (* transport->vtable->handle_watch) (transport, watch, condition);
+ _dbus_watch_unref (watch);
+ _dbus_transport_unref (transport);
+
+ return retval;
+}
+
+/**
+ * Sets the connection using this transport. Allows the transport
+ * to add watches to the connection, queue incoming messages,
+ * and pull outgoing messages.
+ *
+ * @param transport the transport.
+ * @param connection the connection.
+ * @returns #FALSE if not enough memory
+ */
+dbus_bool_t
+_dbus_transport_set_connection (DBusTransport *transport,
+ DBusConnection *connection)
+{
+ _dbus_assert (transport->vtable->connection_set != NULL);
+ _dbus_assert (transport->connection == NULL);
+
+ transport->connection = connection;
+
+ _dbus_transport_ref (transport);
+ if (!(* transport->vtable->connection_set) (transport))
+ transport->connection = NULL;
+ _dbus_transport_unref (transport);
+
+ return transport->connection != NULL;
+}
+
+/**
+ * Get the socket file descriptor, if any.
+ *
+ * @param transport the transport
+ * @param fd_p pointer to fill in with the descriptor
+ * @returns #TRUE if a descriptor was available
+ */
+dbus_bool_t
+_dbus_transport_get_socket_fd (DBusTransport *transport,
+ DBusSocket *fd_p)
+{
+ dbus_bool_t retval;
+
+ if (transport->vtable->get_socket_fd == NULL)
+ return FALSE;
+
+ if (transport->disconnected)
+ return FALSE;
+
+ _dbus_transport_ref (transport);
+
+ retval = (* transport->vtable->get_socket_fd) (transport,
+ fd_p);
+
+ _dbus_transport_unref (transport);
+
+ return retval;
+}
+
+/**
+ * Performs a single poll()/select() on the transport's file
+ * descriptors and then reads/writes data as appropriate,
+ * queueing incoming messages and sending outgoing messages.
+ * This is the backend for _dbus_connection_do_iteration().
+ * See _dbus_connection_do_iteration() for full details.
+ *
+ * @param transport the transport.
+ * @param flags indicates whether to read or write, and whether to block.
+ * @param timeout_milliseconds if blocking, timeout or -1 for no timeout.
+ */
+void
+_dbus_transport_do_iteration (DBusTransport *transport,
+ unsigned int flags,
+ int timeout_milliseconds)
+{
+ _dbus_assert (transport->vtable->do_iteration != NULL);
+
+ _dbus_verbose ("Transport iteration flags 0x%x timeout %d connected = %d\n",
+ flags, timeout_milliseconds, !transport->disconnected);
+
+ if ((flags & (DBUS_ITERATION_DO_WRITING |
+ DBUS_ITERATION_DO_READING)) == 0)
+ return; /* Nothing to do */
+
+ if (transport->disconnected)
+ return;
+
+ _dbus_transport_ref (transport);
+ (* transport->vtable->do_iteration) (transport, flags,
+ timeout_milliseconds);
+ _dbus_transport_unref (transport);
+
+ _dbus_verbose ("end\n");
+}
+
+static dbus_bool_t
+recover_unused_bytes (DBusTransport *transport)
+{
+ if (_dbus_auth_needs_decoding (transport->auth))
+ {
+ DBusString plaintext;
+ const DBusString *encoded;
+ DBusString *buffer;
+ int orig_len;
+
+ if (!_dbus_string_init (&plaintext))
+ goto nomem;
+
+ _dbus_auth_get_unused_bytes (transport->auth,
+ &encoded);
+
+ if (!_dbus_auth_decode_data (transport->auth,
+ encoded, &plaintext))
+ {
+ _dbus_string_free (&plaintext);
+ goto nomem;
+ }
+
+ _dbus_message_loader_get_buffer (transport->loader,
+ &buffer,
+ NULL,
+ NULL);
+
+ orig_len = _dbus_string_get_length (buffer);
+
+ if (!_dbus_string_move (&plaintext, 0, buffer,
+ orig_len))
+ {
+ _dbus_string_free (&plaintext);
+ goto nomem;
+ }
+
+ _dbus_verbose (" %d unused bytes sent to message loader\n",
+ _dbus_string_get_length (buffer) -
+ orig_len);
+
+ _dbus_message_loader_return_buffer (transport->loader,
+ buffer);
+
+ _dbus_auth_delete_unused_bytes (transport->auth);
+
+ _dbus_string_free (&plaintext);
+ }
+ else
+ {
+ const DBusString *bytes;
+ DBusString *buffer;
+#ifdef DBUS_ENABLE_VERBOSE_MODE
+ int orig_len;
+#endif
+ dbus_bool_t succeeded;
+
+ _dbus_message_loader_get_buffer (transport->loader,
+ &buffer,
+ NULL,
+ NULL);
+
+#ifdef DBUS_ENABLE_VERBOSE_MODE
+ orig_len = _dbus_string_get_length (buffer);
+#endif
+
+ _dbus_auth_get_unused_bytes (transport->auth,
+ &bytes);
+
+ succeeded = TRUE;
+ if (!_dbus_string_copy (bytes, 0, buffer, _dbus_string_get_length (buffer)))
+ succeeded = FALSE;
+
+ _dbus_verbose (" %d unused bytes sent to message loader\n",
+ _dbus_string_get_length (buffer) -
+ orig_len);
+
+ _dbus_message_loader_return_buffer (transport->loader,
+ buffer);
+
+ if (succeeded)
+ _dbus_auth_delete_unused_bytes (transport->auth);
+ else
+ goto nomem;
+ }
+
+ return TRUE;
+
+ nomem:
+ _dbus_verbose ("Not enough memory to transfer unused bytes from auth conversation\n");
+ return FALSE;
+}
+
+/**
+ * Reports our current dispatch status (whether there's buffered
+ * data to be queued as messages, or not, or we need memory).
+ *
+ * @param transport the transport
+ * @returns current status
+ */
+DBusDispatchStatus
+_dbus_transport_get_dispatch_status (DBusTransport *transport)
+{
+ if (_dbus_counter_get_size_value (transport->live_messages) >= transport->max_live_messages_size ||
+ _dbus_counter_get_unix_fd_value (transport->live_messages) >= transport->max_live_messages_unix_fds)
+ return DBUS_DISPATCH_COMPLETE; /* complete for now */
+
+ if (!_dbus_transport_try_to_authenticate (transport))
+ {
+ if (_dbus_auth_do_work (transport->auth) ==
+ DBUS_AUTH_STATE_WAITING_FOR_MEMORY)
+ return DBUS_DISPATCH_NEED_MEMORY;
+ else if (!_dbus_transport_try_to_authenticate (transport))
+ return DBUS_DISPATCH_COMPLETE;
+ }
+
+ if (!transport->unused_bytes_recovered &&
+ !recover_unused_bytes (transport))
+ return DBUS_DISPATCH_NEED_MEMORY;
+
+ transport->unused_bytes_recovered = TRUE;
+
+ if (!_dbus_message_loader_queue_messages (transport->loader))
+ return DBUS_DISPATCH_NEED_MEMORY;
+
+ if (_dbus_message_loader_peek_message (transport->loader) != NULL)
+ return DBUS_DISPATCH_DATA_REMAINS;
+ else
+ return DBUS_DISPATCH_COMPLETE;
+}
+
+/**
+ * Processes data we've read while handling a watch, potentially
+ * converting some of it to messages and queueing those messages on
+ * the connection.
+ *
+ * @param transport the transport
+ * @returns #TRUE if we had enough memory to queue all messages
+ */
+dbus_bool_t
+_dbus_transport_queue_messages (DBusTransport *transport)
+{
+ DBusDispatchStatus status;
+
+#if 0
+ _dbus_verbose ("enter\n");
+#endif
+
+ /* Queue any messages */
+ while ((status = _dbus_transport_get_dispatch_status (transport)) == DBUS_DISPATCH_DATA_REMAINS)
+ {
+ DBusMessage *message;
+ DBusList *link;
+
+ link = _dbus_message_loader_pop_message_link (transport->loader);
+ _dbus_assert (link != NULL);
+
+ message = link->data;
+
+ _dbus_verbose ("queueing received message %p\n", message);
+
+ if (!_dbus_message_add_counter (message, transport->live_messages))
+ {
+ _dbus_message_loader_putback_message_link (transport->loader,
+ link);
+ status = DBUS_DISPATCH_NEED_MEMORY;
+ break;
+ }
+ else
+ {
+ /* We didn't call the notify function when we added the counter, so
+ * catch up now. Since we have the connection's lock, it's desirable
+ * that we bypass the notify function and call this virtual method
+ * directly. */
+ if (transport->vtable->live_messages_changed)
+ (* transport->vtable->live_messages_changed) (transport);
+
+ /* pass ownership of link and message ref to connection */
+ _dbus_connection_queue_received_message_link (transport->connection,
+ link);
+ }
+ }
+
+ if (_dbus_message_loader_get_is_corrupted (transport->loader))
+ {
+ _dbus_verbose ("Corrupted message stream, disconnecting\n");
+ _dbus_transport_disconnect (transport);
+ }
+
+ return status != DBUS_DISPATCH_NEED_MEMORY;
+}
+
+/**
+ * See dbus_connection_set_max_message_size().
+ *
+ * @param transport the transport
+ * @param size the max size of a single message
+ */
+void
+_dbus_transport_set_max_message_size (DBusTransport *transport,
+ long size)
+{
+ _dbus_message_loader_set_max_message_size (transport->loader, size);
+}
+
+/**
+ * See dbus_connection_set_max_message_unix_fds().
+ *
+ * @param transport the transport
+ * @param n the max number of unix fds of a single message
+ */
+void
+_dbus_transport_set_max_message_unix_fds (DBusTransport *transport,
+ long n)
+{
+ _dbus_message_loader_set_max_message_unix_fds (transport->loader, n);
+}
+
+/**
+ * See dbus_connection_get_max_message_size().
+ *
+ * @param transport the transport
+ * @returns max message size
+ */
+long
+_dbus_transport_get_max_message_size (DBusTransport *transport)
+{
+ return _dbus_message_loader_get_max_message_size (transport->loader);
+}
+
+/**
+ * See dbus_connection_get_max_message_unix_fds().
+ *
+ * @param transport the transport
+ * @returns max message unix fds
+ */
+long
+_dbus_transport_get_max_message_unix_fds (DBusTransport *transport)
+{
+ return _dbus_message_loader_get_max_message_unix_fds (transport->loader);
+}
+
+/**
+ * See dbus_connection_set_max_received_size().
+ *
+ * @param transport the transport
+ * @param size the max size of all incoming messages
+ */
+void
+_dbus_transport_set_max_received_size (DBusTransport *transport,
+ long size)
+{
+ transport->max_live_messages_size = size;
+ _dbus_counter_set_notify (transport->live_messages,
+ transport->max_live_messages_size,
+ transport->max_live_messages_unix_fds,
+ live_messages_notify,
+ transport);
+}
+
+/**
+ * See dbus_connection_set_max_received_unix_fds().
+ *
+ * @param transport the transport
+ * @param n the max unix fds of all incoming messages
+ */
+void
+_dbus_transport_set_max_received_unix_fds (DBusTransport *transport,
+ long n)
+{
+ transport->max_live_messages_unix_fds = n;
+ _dbus_counter_set_notify (transport->live_messages,
+ transport->max_live_messages_size,
+ transport->max_live_messages_unix_fds,
+ live_messages_notify,
+ transport);
+}
+
+/**
+ * See dbus_connection_get_max_received_size().
+ *
+ * @param transport the transport
+ * @returns max bytes for all live messages
+ */
+long
+_dbus_transport_get_max_received_size (DBusTransport *transport)
+{
+ return transport->max_live_messages_size;
+}
+
+/**
+ * See dbus_connection_set_max_received_unix_fds().
+ *
+ * @param transport the transport
+ * @returns max unix fds for all live messages
+ */
+long
+_dbus_transport_get_max_received_unix_fds (DBusTransport *transport)
+{
+ return transport->max_live_messages_unix_fds;
+}
+
+/**
+ * See dbus_connection_get_unix_user().
+ *
+ * @param transport the transport
+ * @param uid return location for the user ID
+ * @returns #TRUE if uid is filled in with a valid user ID
+ */
+dbus_bool_t
+_dbus_transport_get_unix_user (DBusTransport *transport,
+ unsigned long *uid)
+{
+ DBusCredentials *auth_identity;
+
+ *uid = _DBUS_INT32_MAX; /* better than some root or system user in
+ * case of bugs in the caller. Caller should
+ * never use this value on purpose, however.
+ */
+
+ if (!transport->authenticated)
+ return FALSE;
+
+ auth_identity = _dbus_auth_get_identity (transport->auth);
+
+ if (_dbus_credentials_include (auth_identity,
+ DBUS_CREDENTIAL_UNIX_USER_ID))
+ {
+ *uid = _dbus_credentials_get_unix_uid (auth_identity);
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+/**
+ * See dbus_connection_get_unix_process_id().
+ *
+ * @param transport the transport
+ * @param pid return location for the process ID
+ * @returns #TRUE if uid is filled in with a valid process ID
+ */
+dbus_bool_t
+_dbus_transport_get_unix_process_id (DBusTransport *transport,
+ unsigned long *pid)
+{
+ DBusCredentials *auth_identity;
+
+ *pid = DBUS_PID_UNSET; /* Caller should never use this value on purpose,
+ * but we set it to a safe number, INT_MAX,
+ * just to root out possible bugs in bad callers.
+ */
+
+ if (!transport->authenticated)
+ return FALSE;
+
+ auth_identity = _dbus_auth_get_identity (transport->auth);
+
+ if (_dbus_credentials_include (auth_identity,
+ DBUS_CREDENTIAL_UNIX_PROCESS_ID))
+ {
+ *pid = _dbus_credentials_get_pid (auth_identity);
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+/**
+ * See dbus_connection_get_adt_audit_session_data().
+ *
+ * @param transport the transport
+ * @param data return location for the ADT audit data
+ * @param data_size return length of audit data
+ * @returns #TRUE if audit data is filled in with a valid ucred
+ */
+dbus_bool_t
+_dbus_transport_get_adt_audit_session_data (DBusTransport *transport,
+ void **data,
+ int *data_size)
+{
+ DBusCredentials *auth_identity;
+
+ *data = NULL;
+ *data_size = 0;
+
+ if (!transport->authenticated)
+ return FALSE;
+
+ auth_identity = _dbus_auth_get_identity (transport->auth);
+
+ if (_dbus_credentials_include (auth_identity,
+ DBUS_CREDENTIAL_ADT_AUDIT_DATA_ID))
+ {
+ *data = (void *) _dbus_credentials_get_adt_audit_data (auth_identity);
+ *data_size = _dbus_credentials_get_adt_audit_data_size (auth_identity);
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+/**
+ * See dbus_connection_set_unix_user_function().
+ *
+ * @param transport the transport
+ * @param function the predicate
+ * @param data data to pass to the predicate
+ * @param free_data_function function to free the data
+ * @param old_data the old user data to be freed
+ * @param old_free_data_function old free data function to free it with
+ */
+void
+_dbus_transport_set_unix_user_function (DBusTransport *transport,
+ DBusAllowUnixUserFunction function,
+ void *data,
+ DBusFreeFunction free_data_function,
+ void **old_data,
+ DBusFreeFunction *old_free_data_function)
+{
+ *old_data = transport->unix_user_data;
+ *old_free_data_function = transport->free_unix_user_data;
+
+ transport->unix_user_function = function;
+ transport->unix_user_data = data;
+ transport->free_unix_user_data = free_data_function;
+}
+
+dbus_bool_t
+_dbus_transport_get_linux_security_label (DBusTransport *transport,
+ char **label_p)
+{
+ DBusCredentials *auth_identity;
+
+ *label_p = NULL;
+
+ if (!transport->authenticated)
+ return FALSE;
+
+ auth_identity = _dbus_auth_get_identity (transport->auth);
+
+ if (_dbus_credentials_include (auth_identity,
+ DBUS_CREDENTIAL_LINUX_SECURITY_LABEL))
+ {
+ /* If no memory, we are supposed to return TRUE and set NULL */
+ *label_p = _dbus_strdup (_dbus_credentials_get_linux_security_label (auth_identity));
+
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+/**
+ * If the transport has already been authenticated, return its
+ * credentials. If not, return #NULL.
+ *
+ * The caller must ref the returned credentials object if it wants to
+ * keep it.
+ */
+DBusCredentials *
+_dbus_transport_get_credentials (DBusTransport *transport)
+{
+ if (!transport->authenticated)
+ return FALSE;
+
+ return _dbus_auth_get_identity (transport->auth);
+}
+
+/**
+ * See dbus_connection_get_windows_user().
+ *
+ * @param transport the transport
+ * @param windows_sid_p return location for the user ID
+ * @returns #TRUE if user is available; the returned value may still be #NULL if no memory to copy it
+ */
+dbus_bool_t
+_dbus_transport_get_windows_user (DBusTransport *transport,
+ char **windows_sid_p)
+{
+ DBusCredentials *auth_identity;
+
+ *windows_sid_p = NULL;
+
+ if (!transport->authenticated)
+ return FALSE;
+
+ auth_identity = _dbus_auth_get_identity (transport->auth);
+
+ if (_dbus_credentials_include (auth_identity,
+ DBUS_CREDENTIAL_WINDOWS_SID))
+ {
+ /* If no memory, we are supposed to return TRUE and set NULL */
+ *windows_sid_p = _dbus_strdup (_dbus_credentials_get_windows_sid (auth_identity));
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+/**
+ * See dbus_connection_set_windows_user_function().
+ *
+ * @param transport the transport
+ * @param function the predicate
+ * @param data data to pass to the predicate
+ * @param free_data_function function to free the data
+ * @param old_data the old user data to be freed
+ * @param old_free_data_function old free data function to free it with
+ */
+
+void
+_dbus_transport_set_windows_user_function (DBusTransport *transport,
+ DBusAllowWindowsUserFunction function,
+ void *data,
+ DBusFreeFunction free_data_function,
+ void **old_data,
+ DBusFreeFunction *old_free_data_function)
+{
+ *old_data = transport->windows_user_data;
+ *old_free_data_function = transport->free_windows_user_data;
+
+ transport->windows_user_function = function;
+ transport->windows_user_data = data;
+ transport->free_windows_user_data = free_data_function;
+}
+
+/**
+ * Sets the SASL authentication mechanisms supported by this transport.
+ *
+ * @param transport the transport
+ * @param mechanisms the #NULL-terminated array of mechanisms
+ *
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_transport_set_auth_mechanisms (DBusTransport *transport,
+ const char **mechanisms)
+{
+ return _dbus_auth_set_mechanisms (transport->auth, mechanisms);
+}
+
+/**
+ * See dbus_connection_set_allow_anonymous()
+ *
+ * @param transport the transport
+ * @param value #TRUE to allow anonymous connection
+ */
+void
+_dbus_transport_set_allow_anonymous (DBusTransport *transport,
+ dbus_bool_t value)
+{
+ transport->allow_anonymous = value != FALSE;
+}
+
+/**
+ * Return how many file descriptors are pending in the loader
+ *
+ * @param transport the transport
+ */
+int
+_dbus_transport_get_pending_fds_count (DBusTransport *transport)
+{
+ return _dbus_message_loader_get_pending_fds_count (transport->loader);
+}
+
+/**
+ * Register a function to be called whenever the number of pending file
+ * descriptors in the loader change.
+ *
+ * @param transport the transport
+ * @param callback the callback
+ */
+void
+_dbus_transport_set_pending_fds_function (DBusTransport *transport,
+ void (* callback) (void *),
+ void *data)
+{
+ _dbus_message_loader_set_pending_fds_function (transport->loader,
+ callback, data);
+}
+
+#ifdef DBUS_ENABLE_STATS
+void
+_dbus_transport_get_stats (DBusTransport *transport,
+ dbus_uint32_t *queue_bytes,
+ dbus_uint32_t *queue_fds,
+ dbus_uint32_t *peak_queue_bytes,
+ dbus_uint32_t *peak_queue_fds)
+{
+ if (queue_bytes != NULL)
+ *queue_bytes = _dbus_counter_get_size_value (transport->live_messages);
+
+ if (queue_fds != NULL)
+ *queue_fds = _dbus_counter_get_unix_fd_value (transport->live_messages);
+
+ if (peak_queue_bytes != NULL)
+ *peak_queue_bytes = _dbus_counter_get_peak_size_value (transport->live_messages);
+
+ if (peak_queue_fds != NULL)
+ *peak_queue_fds = _dbus_counter_get_peak_unix_fd_value (transport->live_messages);
+}
+#endif /* DBUS_ENABLE_STATS */
+
+/** @} */
diff --git a/src/3rdparty/libdbus/dbus/dbus-transport.h b/src/3rdparty/libdbus/dbus/dbus-transport.h
new file mode 100644
index 00000000..24fd1ac8
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-transport.h
@@ -0,0 +1,121 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-transport.h DBusTransport object (internal to D-BUS implementation)
+ *
+ * Copyright (C) 2002, 2004 Red Hat Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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
+ *
+ */
+#ifndef DBUS_TRANSPORT_H
+#define DBUS_TRANSPORT_H
+
+#include <dbus/dbus-internals.h>
+#include <dbus/dbus-connection.h>
+#include <dbus/dbus-credentials.h>
+#include <dbus/dbus-protocol.h>
+#include <dbus/dbus-address.h>
+
+DBUS_BEGIN_DECLS
+
+typedef struct DBusTransport DBusTransport;
+
+DBusTransport* _dbus_transport_open (DBusAddressEntry *entry,
+ DBusError *error);
+DBusTransport* _dbus_transport_ref (DBusTransport *transport);
+void _dbus_transport_unref (DBusTransport *transport);
+void _dbus_transport_disconnect (DBusTransport *transport);
+dbus_bool_t _dbus_transport_get_is_connected (DBusTransport *transport);
+dbus_bool_t _dbus_transport_peek_is_authenticated (DBusTransport *transport);
+dbus_bool_t _dbus_transport_try_to_authenticate (DBusTransport *transport);
+dbus_bool_t _dbus_transport_get_is_anonymous (DBusTransport *transport);
+dbus_bool_t _dbus_transport_can_pass_unix_fd (DBusTransport *transport);
+
+const char* _dbus_transport_get_address (DBusTransport *transport);
+const char* _dbus_transport_get_server_id (DBusTransport *transport);
+dbus_bool_t _dbus_transport_handle_watch (DBusTransport *transport,
+ DBusWatch *watch,
+ unsigned int condition);
+dbus_bool_t _dbus_transport_set_connection (DBusTransport *transport,
+ DBusConnection *connection);
+void _dbus_transport_do_iteration (DBusTransport *transport,
+ unsigned int flags,
+ int timeout_milliseconds);
+DBusDispatchStatus _dbus_transport_get_dispatch_status (DBusTransport *transport);
+dbus_bool_t _dbus_transport_queue_messages (DBusTransport *transport);
+
+void _dbus_transport_set_max_message_size (DBusTransport *transport,
+ long size);
+long _dbus_transport_get_max_message_size (DBusTransport *transport);
+void _dbus_transport_set_max_received_size (DBusTransport *transport,
+ long size);
+long _dbus_transport_get_max_received_size (DBusTransport *transport);
+
+void _dbus_transport_set_max_message_unix_fds (DBusTransport *transport,
+ long n);
+long _dbus_transport_get_max_message_unix_fds (DBusTransport *transport);
+void _dbus_transport_set_max_received_unix_fds(DBusTransport *transport,
+ long n);
+long _dbus_transport_get_max_received_unix_fds(DBusTransport *transport);
+
+dbus_bool_t _dbus_transport_get_socket_fd (DBusTransport *transport,
+ DBusSocket *fd_p);
+dbus_bool_t _dbus_transport_get_unix_user (DBusTransport *transport,
+ unsigned long *uid);
+dbus_bool_t _dbus_transport_get_unix_process_id (DBusTransport *transport,
+ unsigned long *pid);
+dbus_bool_t _dbus_transport_get_adt_audit_session_data (DBusTransport *transport,
+ void **data,
+ int *data_size);
+void _dbus_transport_set_unix_user_function (DBusTransport *transport,
+ DBusAllowUnixUserFunction function,
+ void *data,
+ DBusFreeFunction free_data_function,
+ void **old_data,
+ DBusFreeFunction *old_free_data_function);
+dbus_bool_t _dbus_transport_get_windows_user (DBusTransport *transport,
+ char **windows_sid_p);
+dbus_bool_t _dbus_transport_get_linux_security_label (DBusTransport *transport,
+ char **label_p);
+DBusCredentials *_dbus_transport_get_credentials (DBusTransport *transport);
+
+void _dbus_transport_set_windows_user_function (DBusTransport *transport,
+ DBusAllowWindowsUserFunction function,
+ void *data,
+ DBusFreeFunction free_data_function,
+ void **old_data,
+ DBusFreeFunction *old_free_data_function);
+dbus_bool_t _dbus_transport_set_auth_mechanisms (DBusTransport *transport,
+ const char **mechanisms);
+void _dbus_transport_set_allow_anonymous (DBusTransport *transport,
+ dbus_bool_t value);
+int _dbus_transport_get_pending_fds_count (DBusTransport *transport);
+void _dbus_transport_set_pending_fds_function (DBusTransport *transport,
+ void (* callback) (void *),
+ void *data);
+
+/* if DBUS_ENABLE_STATS */
+void _dbus_transport_get_stats (DBusTransport *transport,
+ dbus_uint32_t *queue_bytes,
+ dbus_uint32_t *queue_fds,
+ dbus_uint32_t *peak_queue_bytes,
+ dbus_uint32_t *peak_queue_fds);
+
+DBUS_END_DECLS
+
+#endif /* DBUS_TRANSPORT_H */
diff --git a/src/3rdparty/libdbus/dbus/dbus-types.h b/src/3rdparty/libdbus/dbus/dbus-types.h
new file mode 100644
index 00000000..5ace2733
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-types.h
@@ -0,0 +1,179 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-types.h types such as dbus_bool_t
+ *
+ * Copyright (C) 2002 Red Hat Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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
+ *
+ */
+#if !defined (DBUS_INSIDE_DBUS_H) && !defined (DBUS_COMPILATION)
+#error "Only <dbus/dbus.h> can be included directly, this file may disappear or change contents."
+#endif
+
+#ifndef DBUS_TYPES_H
+#define DBUS_TYPES_H
+
+#include <stddef.h>
+#include <dbus/dbus-arch-deps.h>
+
+typedef dbus_uint32_t dbus_unichar_t;
+/* boolean size must be fixed at 4 bytes due to wire protocol! */
+typedef dbus_uint32_t dbus_bool_t;
+
+/* Normally docs are in .c files, but there isn't a .c file for this. */
+/**
+ * @defgroup DBusTypes Basic types
+ * @ingroup DBus
+ * @brief dbus_bool_t, dbus_int32_t, etc.
+ *
+ * Typedefs for common primitive types.
+ *
+ * @{
+ */
+
+/**
+ * @typedef dbus_bool_t
+ *
+ * A boolean, valid values are #TRUE and #FALSE.
+ */
+
+/**
+ * @typedef dbus_uint32_t
+ *
+ * A 32-bit unsigned integer on all platforms.
+ */
+
+/**
+ * @typedef dbus_int32_t
+ *
+ * A 32-bit signed integer on all platforms.
+ */
+
+/**
+ * @typedef dbus_uint16_t
+ *
+ * A 16-bit unsigned integer on all platforms.
+ */
+
+/**
+ * @typedef dbus_int16_t
+ *
+ * A 16-bit signed integer on all platforms.
+ */
+
+
+/**
+ * @typedef dbus_uint64_t
+ *
+ * A 64-bit unsigned integer.
+ */
+
+/**
+ * @typedef dbus_int64_t
+ *
+ * A 64-bit signed integer.
+ */
+
+/**
+ * @def DBUS_HAVE_INT64
+ *
+ * Always defined.
+ *
+ * In older libdbus versions, this would be undefined if there was no
+ * 64-bit integer type on that platform. libdbus no longer supports
+ * such platforms.
+ */
+
+/**
+ * @def DBUS_INT64_CONSTANT
+ *
+ * Declare a 64-bit signed integer constant. The macro
+ * adds the necessary "LL" or whatever after the integer,
+ * giving a literal such as "325145246765LL"
+ */
+
+/**
+ * @def DBUS_UINT64_CONSTANT
+ *
+ * Declare a 64-bit unsigned integer constant. The macro
+ * adds the necessary "ULL" or whatever after the integer,
+ * giving a literal such as "325145246765ULL"
+ */
+
+/**
+ * @def DBUS_INT64_MODIFIER
+ *
+ * A string literal for a length modifier that is appropriate to print
+ * the #dbus_int64_t and #dbus_uint64_t types.
+ * For example, it might be an empty string, "l", "ll", or "I64".
+ *
+ * This modifier needs to be concatenated with a literal "%" and a
+ * conversion specifier that can print signed or unsigned integers,
+ * for example:
+ *
+ * @code
+ * dbus_int64_t i = -123;
+ * dbus_uint64_t u = 456;
+ *
+ * printf ("signed: %" DBUS_INT64_MODIFIER "d\n", i);
+ * printf ("unsigned decimal: %" DBUS_INT64_MODIFIER "u\n", u);
+ * printf ("unsigned hex: 0x%" DBUS_INT64_MODIFIER "x\n", x);
+ * @endcode
+ */
+
+/**
+ * An 8-byte struct you could use to access int64 without having
+ * int64 support. Use #dbus_int64_t or #dbus_uint64_t instead.
+ */
+typedef struct
+{
+ dbus_uint32_t first32; /**< first 32 bits in the 8 bytes (beware endian issues) */
+ dbus_uint32_t second32; /**< second 32 bits in the 8 bytes (beware endian issues) */
+} DBus8ByteStruct;
+
+/**
+ * A simple value union that lets you access bytes as if they
+ * were various types; useful when dealing with basic types via
+ * void pointers and varargs.
+ *
+ * This union also contains a pointer member (which can be used
+ * to retrieve a string from dbus_message_iter_get_basic(), for
+ * instance), so on future platforms it could conceivably be larger
+ * than 8 bytes.
+ */
+typedef union
+{
+ unsigned char bytes[8]; /**< as 8 individual bytes */
+ dbus_int16_t i16; /**< as int16 */
+ dbus_uint16_t u16; /**< as int16 */
+ dbus_int32_t i32; /**< as int32 */
+ dbus_uint32_t u32; /**< as int32 */
+ dbus_bool_t bool_val; /**< as boolean */
+ dbus_int64_t i64; /**< as int64 */
+ dbus_uint64_t u64; /**< as int64 */
+ DBus8ByteStruct eight; /**< as 8-byte struct */
+ double dbl; /**< as double */
+ unsigned char byt; /**< as byte */
+ char *str; /**< as char* (string, object path or signature) */
+ int fd; /**< as Unix file descriptor */
+} DBusBasicValue;
+
+/** @} */
+
+#endif /* DBUS_TYPES_H */
diff --git a/src/3rdparty/libdbus/dbus/dbus-userdb.c b/src/3rdparty/libdbus/dbus/dbus-userdb.c
new file mode 100644
index 00000000..452ff9d7
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-userdb.c
@@ -0,0 +1,735 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-userdb.c User database abstraction
+ *
+ * Copyright (C) 2003, 2004 Red Hat, Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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>
+#define DBUS_USERDB_INCLUDES_PRIVATE 1
+#include "dbus-userdb.h"
+#include "dbus-hash.h"
+#include "dbus-test.h"
+#include "dbus-internals.h"
+#include "dbus-protocol.h"
+#include "dbus-credentials.h"
+#include <string.h>
+
+/* It isn't obvious from its name, but this file is part of the Unix
+ * system-dependent part of libdbus. Windows has a parallel
+ * implementation of some of it in dbus-sysdeps-win.c. */
+#if defined(DBUS_WIN) || !defined(DBUS_UNIX)
+#error "This file only makes sense on Unix OSs"
+#endif
+
+/**
+ * @addtogroup DBusInternalsUtils
+ * @{
+ */
+
+static DBusUserInfo *
+_dbus_user_info_ref (DBusUserInfo *info)
+{
+ _dbus_assert (info->refcount > 0);
+ _dbus_assert (info->refcount < SIZE_MAX);
+ info->refcount++;
+ return info;
+}
+
+/**
+ * Decrements the reference count. If it reaches 0,
+ * frees the given #DBusUserInfo's members with _dbus_user_info_free()
+ * and also calls dbus_free() on the block itself
+ *
+ * @param info the info
+ */
+void
+_dbus_user_info_unref (DBusUserInfo *info)
+{
+ if (info == NULL) /* hash table will pass NULL */
+ return;
+
+ _dbus_assert (info->refcount > 0);
+ _dbus_assert (info->refcount < SIZE_MAX);
+
+ if (--info->refcount > 0)
+ return;
+
+ _dbus_user_info_free (info);
+ dbus_free (info);
+}
+
+/**
+ * Decrements the reference count. If it reaches 0,
+ * frees the given #DBusGroupInfo's members with _dbus_group_info_free()
+ * and also calls dbus_free() on the block itself
+ *
+ * @param info the info
+ */
+void
+_dbus_group_info_unref (DBusGroupInfo *info)
+{
+ if (info == NULL) /* hash table will pass NULL */
+ return;
+
+ _dbus_assert (info->refcount > 0);
+ _dbus_assert (info->refcount < SIZE_MAX);
+
+ if (--info->refcount > 0)
+ return;
+
+ _dbus_group_info_free (info);
+ dbus_free (info);
+}
+
+/**
+ * Frees the members of info
+ * (but not info itself)
+ * @param info the user info struct
+ */
+void
+_dbus_user_info_free (DBusUserInfo *info)
+{
+ dbus_free (info->group_ids);
+ dbus_free (info->username);
+ dbus_free (info->homedir);
+}
+
+/**
+ * Frees the members of info (but not info itself).
+ *
+ * @param info the group info
+ */
+void
+_dbus_group_info_free (DBusGroupInfo *info)
+{
+ dbus_free (info->groupname);
+}
+
+/**
+ * Checks if a given string is actually a number
+ * and converts it if it is
+ *
+ * @param str the string to check
+ * @param num the memory location of the unsigned long to fill in
+ * @returns TRUE if str is a number and num is filled in
+ */
+dbus_bool_t
+_dbus_is_a_number (const DBusString *str,
+ unsigned long *num)
+{
+ int end;
+
+ if (_dbus_string_parse_uint (str, 0, num, &end) &&
+ end == _dbus_string_get_length (str))
+ return TRUE;
+ else
+ return FALSE;
+}
+
+/**
+ * Looks up a uid or username in the user database. Only one of name
+ * or UID can be provided. There are wrapper functions for this that
+ * are better to use, this one does no locking or anything on the
+ * database and otherwise sort of sucks.
+ *
+ * @param db the database
+ * @param uid the user ID or #DBUS_UID_UNSET
+ * @param username username or #NULL
+ * @param error error to fill in
+ * @returns the entry in the database (borrowed, do not free)
+ */
+const DBusUserInfo *
+_dbus_user_database_lookup (DBusUserDatabase *db,
+ dbus_uid_t uid,
+ const DBusString *username,
+ DBusError *error)
+{
+ DBusUserInfo *info;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+ _dbus_assert (uid != DBUS_UID_UNSET || username != NULL);
+
+ /* See if the username is really a number */
+ if (uid == DBUS_UID_UNSET)
+ {
+ unsigned long n;
+
+ if (_dbus_is_a_number (username, &n))
+ uid = n;
+ }
+
+ if (uid != DBUS_UID_UNSET)
+ info = _dbus_hash_table_lookup_uintptr (db->users, uid);
+ else
+ info = _dbus_hash_table_lookup_string (db->users_by_name, _dbus_string_get_const_data (username));
+
+ if (info)
+ {
+ _dbus_verbose ("Using cache for UID "DBUS_UID_FORMAT" information\n",
+ info->uid);
+ return info;
+ }
+ else
+ {
+ if (uid != DBUS_UID_UNSET)
+ _dbus_verbose ("No cache for UID "DBUS_UID_FORMAT"\n",
+ uid);
+ else
+ _dbus_verbose ("No cache for user \"%s\"\n",
+ _dbus_string_get_const_data (username));
+
+ info = dbus_new0 (DBusUserInfo, 1);
+ if (info == NULL)
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ return NULL;
+ }
+ info->refcount = 1;
+
+ if (uid != DBUS_UID_UNSET)
+ {
+ if (!_dbus_user_info_fill_uid (info, uid, error))
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ _dbus_user_info_unref (info);
+ return NULL;
+ }
+ }
+ else
+ {
+ if (!_dbus_user_info_fill (info, username, error))
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ _dbus_user_info_unref (info);
+ return NULL;
+ }
+ }
+
+ /* be sure we don't use these after here */
+ uid = DBUS_UID_UNSET;
+ username = NULL;
+
+ /* insert into hash */
+ if (_dbus_hash_table_insert_uintptr (db->users, info->uid, info))
+ {
+ _dbus_user_info_ref (info);
+ }
+ else
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ _dbus_user_info_unref (info);
+ return NULL;
+ }
+
+ if (_dbus_hash_table_insert_string (db->users_by_name,
+ info->username,
+ info))
+ {
+ _dbus_user_info_ref (info);
+ }
+ else
+ {
+ _dbus_hash_table_remove_uintptr (db->users, info->uid);
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ _dbus_user_info_unref (info);
+ return NULL;
+ }
+
+ _dbus_user_info_unref (info);
+
+ /* Return a borrowed pointer to the DBusUserInfo owned by the
+ * hash tables */
+ return info;
+ }
+}
+
+/* Protected by _DBUS_LOCK_system_users */
+static dbus_bool_t database_locked = FALSE;
+static DBusUserDatabase *system_db = NULL;
+static DBusString process_username;
+static DBusString process_homedir;
+
+static void
+shutdown_system_db (void *data)
+{
+ if (system_db != NULL)
+ _dbus_user_database_unref (system_db);
+ system_db = NULL;
+ _dbus_string_free (&process_username);
+ _dbus_string_free (&process_homedir);
+}
+
+static dbus_bool_t
+init_system_db (void)
+{
+ _dbus_assert (database_locked);
+
+ if (system_db == NULL)
+ {
+ DBusError error = DBUS_ERROR_INIT;
+ const DBusUserInfo *info;
+
+ system_db = _dbus_user_database_new ();
+ if (system_db == NULL)
+ return FALSE;
+
+ if (!_dbus_user_database_get_uid (system_db,
+ _dbus_getuid (),
+ &info,
+ &error))
+ {
+ _dbus_user_database_unref (system_db);
+ system_db = NULL;
+
+ if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
+ {
+ dbus_error_free (&error);
+ return FALSE;
+ }
+ else
+ {
+ /* This really should not happen. */
+ _dbus_warn ("Could not get password database information for UID of current process: %s",
+ error.message);
+ dbus_error_free (&error);
+ return FALSE;
+ }
+ }
+
+ if (!_dbus_string_init (&process_username))
+ {
+ _dbus_user_database_unref (system_db);
+ system_db = NULL;
+ return FALSE;
+ }
+
+ if (!_dbus_string_init (&process_homedir))
+ {
+ _dbus_string_free (&process_username);
+ _dbus_user_database_unref (system_db);
+ system_db = NULL;
+ return FALSE;
+ }
+
+ if (!_dbus_string_append (&process_username,
+ info->username) ||
+ !_dbus_string_append (&process_homedir,
+ info->homedir) ||
+ !_dbus_register_shutdown_func (shutdown_system_db, NULL))
+ {
+ _dbus_string_free (&process_username);
+ _dbus_string_free (&process_homedir);
+ _dbus_user_database_unref (system_db);
+ system_db = NULL;
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+/**
+ * Locks global system user database.
+ */
+dbus_bool_t
+_dbus_user_database_lock_system (void)
+{
+ if (_DBUS_LOCK (system_users))
+ {
+ database_locked = TRUE;
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+/**
+ * Unlocks global system user database.
+ */
+void
+_dbus_user_database_unlock_system (void)
+{
+ database_locked = FALSE;
+ _DBUS_UNLOCK (system_users);
+}
+
+/**
+ * Gets the system global user database;
+ * must be called with lock held (_dbus_user_database_lock_system()).
+ *
+ * @returns the database or #NULL if no memory
+ */
+DBusUserDatabase*
+_dbus_user_database_get_system (void)
+{
+ _dbus_assert (database_locked);
+
+ init_system_db ();
+
+ return system_db;
+}
+
+/**
+ * Flushes the system global user database;
+ */
+void
+_dbus_user_database_flush_system (void)
+{
+ if (!_dbus_user_database_lock_system ())
+ {
+ /* nothing to flush */
+ return;
+ }
+
+ if (system_db != NULL)
+ _dbus_user_database_flush (system_db);
+
+ _dbus_user_database_unlock_system ();
+}
+
+/**
+ * Gets username of user owning current process. The returned string
+ * is valid until dbus_shutdown() is called.
+ *
+ * @param username place to store pointer to username
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_username_from_current_process (const DBusString **username)
+{
+ if (!_dbus_user_database_lock_system ())
+ return FALSE;
+
+ if (!init_system_db ())
+ {
+ _dbus_user_database_unlock_system ();
+ return FALSE;
+ }
+ *username = &process_username;
+ _dbus_user_database_unlock_system ();
+
+ return TRUE;
+}
+
+/**
+ * Gets homedir of user owning current process. The returned string
+ * is valid until dbus_shutdown() is called.
+ *
+ * @param homedir place to store pointer to homedir
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_homedir_from_current_process (const DBusString **homedir)
+{
+ if (!_dbus_user_database_lock_system ())
+ return FALSE;
+
+ if (!init_system_db ())
+ {
+ _dbus_user_database_unlock_system ();
+ return FALSE;
+ }
+ *homedir = &process_homedir;
+ _dbus_user_database_unlock_system ();
+
+ return TRUE;
+}
+
+/**
+ * Gets the home directory for the given user.
+ *
+ * @param uid the uid
+ * @param homedir string to append home directory to
+ * @returns #TRUE if user existed and we appended their homedir
+ */
+dbus_bool_t
+_dbus_homedir_from_uid (dbus_uid_t uid,
+ DBusString *homedir)
+{
+ DBusUserDatabase *db;
+ const DBusUserInfo *info;
+
+ if (uid == _dbus_getuid () && uid == _dbus_geteuid ())
+ {
+ const char *from_environment;
+
+ from_environment = _dbus_getenv ("HOME");
+
+ if (from_environment != NULL)
+ return _dbus_string_append (homedir, from_environment);
+ }
+
+ /* FIXME: this can't distinguish ENOMEM from other errors */
+ if (!_dbus_user_database_lock_system ())
+ return FALSE;
+
+ db = _dbus_user_database_get_system ();
+ if (db == NULL)
+ {
+ _dbus_user_database_unlock_system ();
+ return FALSE;
+ }
+
+ if (!_dbus_user_database_get_uid (db, uid,
+ &info, NULL))
+ {
+ _dbus_user_database_unlock_system ();
+ return FALSE;
+ }
+
+ if (!_dbus_string_append (homedir, info->homedir))
+ {
+ _dbus_user_database_unlock_system ();
+ return FALSE;
+ }
+
+ _dbus_user_database_unlock_system ();
+ return TRUE;
+}
+
+/**
+ * Adds the credentials corresponding to the given username.
+ *
+ * Used among other purposes to parses a desired identity provided
+ * from a client in the auth protocol. On UNIX this means parsing a
+ * UID, on Windows probably parsing an SID string.
+ *
+ * @todo this is broken because it treats OOM and parse error
+ * the same way. Needs a #DBusError.
+ *
+ * @param credentials credentials to fill in
+ * @param username the username
+ * @returns #TRUE if the username existed and we got some credentials
+ */
+dbus_bool_t
+_dbus_credentials_add_from_user (DBusCredentials *credentials,
+ const DBusString *username,
+ DBusCredentialsAddFlags flags,
+ DBusError *error)
+{
+ DBusUserDatabase *db;
+ const DBusUserInfo *info;
+ unsigned long uid = DBUS_UID_UNSET;
+
+ /* Fast-path for the common case: if the "username" is all-numeric,
+ * then it's a Unix uid. This is true regardless of whether that uid
+ * exists in NSS or /etc/passwd or equivalent. */
+ if (_dbus_is_a_number (username, &uid))
+ {
+ _DBUS_STATIC_ASSERT (sizeof (uid) == sizeof (dbus_uid_t));
+
+ if (_dbus_credentials_add_unix_uid (credentials, uid))
+ {
+ return TRUE;
+ }
+ else
+ {
+ _DBUS_SET_OOM (error);
+ return FALSE;
+ }
+ }
+
+ /* If we aren't allowed to look in NSS or /etc/passwd, fail now. */
+ if (!(flags & DBUS_CREDENTIALS_ADD_FLAGS_USER_DATABASE))
+ {
+ dbus_set_error (error, DBUS_ERROR_INVALID_ARGS,
+ "Expected a numeric Unix uid");
+ return FALSE;
+ }
+
+ if (!_dbus_user_database_lock_system ())
+ {
+ _DBUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ db = _dbus_user_database_get_system ();
+ if (db == NULL)
+ {
+ _dbus_user_database_unlock_system ();
+ _DBUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ if (!_dbus_user_database_get_username (db, username,
+ &info, error))
+ {
+ _dbus_user_database_unlock_system ();
+ return FALSE;
+ }
+
+ if (!_dbus_credentials_add_unix_uid(credentials, info->uid))
+ {
+ _dbus_user_database_unlock_system ();
+ _DBUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ _dbus_user_database_unlock_system ();
+ return TRUE;
+}
+
+/**
+ * Creates a new user database object used to look up and
+ * cache user information.
+ * @returns new database, or #NULL on out of memory
+ */
+DBusUserDatabase*
+_dbus_user_database_new (void)
+{
+ DBusUserDatabase *db;
+
+ db = dbus_new0 (DBusUserDatabase, 1);
+ if (db == NULL)
+ return NULL;
+
+ db->refcount = 1;
+
+ db->users = _dbus_hash_table_new (DBUS_HASH_UINTPTR,
+ NULL, (DBusFreeFunction) _dbus_user_info_unref);
+
+ if (db->users == NULL)
+ goto failed;
+
+ db->groups = _dbus_hash_table_new (DBUS_HASH_UINTPTR,
+ NULL, (DBusFreeFunction) _dbus_group_info_unref);
+
+ if (db->groups == NULL)
+ goto failed;
+
+ db->users_by_name = _dbus_hash_table_new (DBUS_HASH_STRING,
+ NULL, (DBusFreeFunction) _dbus_user_info_unref);
+ if (db->users_by_name == NULL)
+ goto failed;
+
+ db->groups_by_name = _dbus_hash_table_new (DBUS_HASH_STRING,
+ NULL, (DBusFreeFunction) _dbus_group_info_unref);
+ if (db->groups_by_name == NULL)
+ goto failed;
+
+ return db;
+
+ failed:
+ _dbus_user_database_unref (db);
+ return NULL;
+}
+
+/**
+ * Flush all information out of the user database.
+ */
+void
+_dbus_user_database_flush (DBusUserDatabase *db)
+{
+ _dbus_hash_table_remove_all(db->users_by_name);
+ _dbus_hash_table_remove_all(db->groups_by_name);
+ _dbus_hash_table_remove_all(db->users);
+ _dbus_hash_table_remove_all(db->groups);
+}
+
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
+/**
+ * Increments refcount of user database.
+ * @param db the database
+ * @returns the database
+ */
+DBusUserDatabase *
+_dbus_user_database_ref (DBusUserDatabase *db)
+{
+ _dbus_assert (db->refcount > 0);
+
+ db->refcount += 1;
+
+ return db;
+}
+#endif /* DBUS_ENABLE_EMBEDDED_TESTS */
+
+/**
+ * Decrements refcount of user database.
+ * @param db the database
+ */
+void
+_dbus_user_database_unref (DBusUserDatabase *db)
+{
+ _dbus_assert (db->refcount > 0);
+
+ db->refcount -= 1;
+ if (db->refcount == 0)
+ {
+ if (db->users)
+ _dbus_hash_table_unref (db->users);
+
+ if (db->groups)
+ _dbus_hash_table_unref (db->groups);
+
+ if (db->users_by_name)
+ _dbus_hash_table_unref (db->users_by_name);
+
+ if (db->groups_by_name)
+ _dbus_hash_table_unref (db->groups_by_name);
+
+ dbus_free (db);
+ }
+}
+
+/**
+ * Gets the user information for the given UID,
+ * returned user info should not be freed.
+ *
+ * @param db user database
+ * @param uid the user ID
+ * @param info return location for const ref to user info
+ * @param error error location
+ * @returns #FALSE if error is set
+ */
+dbus_bool_t
+_dbus_user_database_get_uid (DBusUserDatabase *db,
+ dbus_uid_t uid,
+ const DBusUserInfo **info,
+ DBusError *error)
+{
+ *info = _dbus_user_database_lookup (db, uid, NULL, error);
+ return *info != NULL;
+}
+
+/**
+ * Gets the user information for the given username.
+ *
+ * @param db user database
+ * @param username the user name
+ * @param info return location for const ref to user info
+ * @param error error location
+ * @returns #FALSE if error is set
+ */
+dbus_bool_t
+_dbus_user_database_get_username (DBusUserDatabase *db,
+ const DBusString *username,
+ const DBusUserInfo **info,
+ DBusError *error)
+{
+ *info = _dbus_user_database_lookup (db, DBUS_UID_UNSET, username, error);
+ return *info != NULL;
+}
+
+/** @} */
+
+/* Tests in dbus-userdb-util.c */
diff --git a/src/3rdparty/libdbus/dbus/dbus-userdb.h b/src/3rdparty/libdbus/dbus/dbus-userdb.h
new file mode 100644
index 00000000..d37d2433
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-userdb.h
@@ -0,0 +1,124 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-userdb.h User database abstraction
+ *
+ * Copyright (C) 2003 Red Hat, Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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
+ *
+ */
+
+#ifndef DBUS_USERDB_H
+#define DBUS_USERDB_H
+
+#include <dbus/dbus-sysdeps-unix.h>
+
+#ifdef DBUS_WIN
+#error "Don't include this on Windows"
+#endif
+
+DBUS_BEGIN_DECLS
+
+typedef struct DBusUserDatabase DBusUserDatabase;
+
+#ifdef DBUS_USERDB_INCLUDES_PRIVATE
+#include <dbus/dbus-hash.h>
+
+/**
+ * Internals of DBusUserDatabase
+ */
+struct DBusUserDatabase
+{
+ int refcount; /**< Reference count */
+
+ DBusHashTable *users; /**< Users in the database by UID */
+ DBusHashTable *groups; /**< Groups in the database by GID */
+ DBusHashTable *users_by_name; /**< Users in the database by name */
+ DBusHashTable *groups_by_name; /**< Groups in the database by name */
+
+};
+
+
+DBusUserDatabase* _dbus_user_database_new (void);
+DBusUserDatabase* _dbus_user_database_ref (DBusUserDatabase *db);
+void _dbus_user_database_flush (DBusUserDatabase *db);
+void _dbus_user_database_unref (DBusUserDatabase *db);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_user_database_get_uid (DBusUserDatabase *db,
+ dbus_uid_t uid,
+ const DBusUserInfo **info,
+ DBusError *error);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_user_database_get_username (DBusUserDatabase *db,
+ const DBusString *username,
+ const DBusUserInfo **info,
+ DBusError *error);
+DBUS_PRIVATE_EXPORT
+const DBusUserInfo *_dbus_user_database_lookup (DBusUserDatabase *db,
+ dbus_uid_t uid,
+ const DBusString *username,
+ DBusError *error);
+DBUS_PRIVATE_EXPORT
+const DBusGroupInfo* _dbus_user_database_lookup_group (DBusUserDatabase *db,
+ dbus_gid_t gid,
+ const DBusString *groupname,
+ DBusError *error);
+
+void _dbus_user_info_unref (DBusUserInfo *info);
+DBUS_PRIVATE_EXPORT
+void _dbus_group_info_unref (DBusGroupInfo *info);
+#endif /* DBUS_USERDB_INCLUDES_PRIVATE */
+
+DBUS_PRIVATE_EXPORT
+DBusUserDatabase* _dbus_user_database_get_system (void);
+DBUS_PRIVATE_EXPORT _DBUS_WARN_UNUSED_RESULT
+dbus_bool_t _dbus_user_database_lock_system (void);
+DBUS_PRIVATE_EXPORT
+void _dbus_user_database_unlock_system (void);
+void _dbus_user_database_flush_system (void);
+
+dbus_bool_t _dbus_get_user_id (const DBusString *username,
+ dbus_uid_t *uid);
+dbus_bool_t _dbus_get_group_id (const DBusString *group_name,
+ dbus_gid_t *gid);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_get_user_id_and_primary_group (const DBusString *username,
+ dbus_uid_t *uid_p,
+ dbus_gid_t *gid_p);
+dbus_bool_t _dbus_groups_from_uid (dbus_uid_t uid,
+ dbus_gid_t **group_ids,
+ int *n_group_ids,
+ DBusError *error);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_is_console_user (dbus_uid_t uid,
+ DBusError *error);
+
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_is_a_number (const DBusString *str,
+ unsigned long *num);
+
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_username_from_current_process (const DBusString **username);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_homedir_from_current_process (const DBusString **homedir);
+dbus_bool_t _dbus_homedir_from_uid (dbus_uid_t uid,
+ DBusString *homedir);
+
+DBUS_END_DECLS
+
+#endif /* DBUS_USERDB_H */
diff --git a/src/3rdparty/libdbus/dbus/dbus-uuidgen.c b/src/3rdparty/libdbus/dbus/dbus-uuidgen.c
new file mode 100644
index 00000000..4a5aa13f
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-uuidgen.c
@@ -0,0 +1,132 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-uuidgen.c The guts of the dbus-uuidgen binary live in libdbus, in this file.
+ *
+ * Copyright (C) 2006 Red Hat, Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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-uuidgen.h"
+#include "dbus-internals.h"
+#include "dbus-string.h"
+#include "dbus-protocol.h"
+
+#ifdef DBUS_WIN
+#error "dbus-uuidgen should not be needed on Windows"
+#endif
+
+/**
+ * @defgroup DBusInternalsUuidgen dbus-uuidgen implementation
+ * @ingroup DBusInternals
+ * @brief Functions for dbus-uuidgen binary
+ *
+ * These are not considered part of the ABI, and if you call them
+ * you will get screwed by future changes.
+ *
+ * @{
+ */
+
+static dbus_bool_t
+return_uuid (DBusGUID *uuid,
+ char **uuid_p,
+ DBusError *error)
+{
+ if (uuid_p)
+ {
+ DBusString encoded;
+
+ if (!_dbus_string_init (&encoded))
+ {
+ _DBUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ if (!_dbus_uuid_encode (uuid, &encoded) ||
+ !_dbus_string_steal_data (&encoded, uuid_p))
+ {
+ _DBUS_SET_OOM (error);
+ _dbus_string_free (&encoded);
+ return FALSE;
+ }
+ _dbus_string_free (&encoded);
+ }
+ return TRUE;
+}
+
+/**
+ * For use by the dbus-uuidgen binary ONLY, do not call this.
+ * We can and will change this function without modifying
+ * the libdbus soname.
+ *
+ * @param filename the file or #NULL for the machine ID file
+ * @param uuid_p out param to return the uuid
+ * @param create_if_not_found whether to create it if not already there
+ * @param error error return
+ * @returns #FALSE if error is set
+ */
+dbus_bool_t
+_dbus_get_uuid (const char *filename,
+ char **uuid_p,
+ dbus_bool_t create_if_not_found,
+ DBusError *error)
+{
+ DBusGUID uuid;
+
+ if (filename)
+ {
+ DBusString filename_str;
+ _dbus_string_init_const (&filename_str, filename);
+ if (!_dbus_read_uuid_file (&filename_str, &uuid, create_if_not_found, error))
+ goto error;
+ }
+ else
+ {
+ if (!_dbus_read_local_machine_uuid (&uuid, create_if_not_found, error))
+ goto error;
+ }
+
+ if (!return_uuid(&uuid, uuid_p, error))
+ goto error;
+
+ return TRUE;
+
+ error:
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ return FALSE;
+}
+
+/**
+ * @param uuid_p out param to return the uuid
+ * @param error location to store reason for failure
+ * @returns #TRUE on success
+ */
+dbus_bool_t
+_dbus_create_uuid (char **uuid_p,
+ DBusError *error)
+{
+ DBusGUID uuid;
+
+ if (!_dbus_generate_uuid (&uuid, error))
+ return FALSE;
+
+ return return_uuid (&uuid, uuid_p, error);
+}
+
+/** @} */
diff --git a/src/3rdparty/libdbus/dbus/dbus-uuidgen.h b/src/3rdparty/libdbus/dbus/dbus-uuidgen.h
new file mode 100644
index 00000000..b74d9e0e
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-uuidgen.h
@@ -0,0 +1,49 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-uuidgen.h The guts of the dbus-uuidgen binary live in libdbus, in this file.
+ *
+ * Copyright (C) 2006 Red Hat, Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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
+ *
+ */
+#ifdef DBUS_INSIDE_DBUS_H
+#error "You can't include dbus-uuidgen.h in the public header dbus.h"
+#endif
+
+#ifndef DBUS_UUIDGEN_H
+#define DBUS_UUIDGEN_H
+
+#include <dbus/dbus-macros-internal.h>
+#include <dbus/dbus-types.h>
+#include <dbus/dbus-errors.h>
+
+DBUS_BEGIN_DECLS
+
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_get_uuid (const char *filename,
+ char **uuid_p,
+ dbus_bool_t create_if_not_found,
+ DBusError *error);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_create_uuid (char **uuid_p,
+ DBusError *error);
+
+DBUS_END_DECLS
+
+#endif /* DBUS_UUIDGEN_H */
diff --git a/src/3rdparty/libdbus/dbus/dbus-valgrind-internal.h b/src/3rdparty/libdbus/dbus/dbus-valgrind-internal.h
new file mode 100644
index 00000000..4c3f3ff7
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-valgrind-internal.h
@@ -0,0 +1,69 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-valgrind-internal.h - valgrind glue
+ *
+ * Copyright © 2011 Nokia Corporation
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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
+ *
+ */
+
+#ifndef DBUS_VALGRIND_INTERNAL_H
+#define DBUS_VALGRIND_INTERNAL_H
+
+#include "config.h"
+#include "dbus-internals.h"
+
+#ifdef WITH_VALGRIND
+# include <memcheck.h>
+# include <valgrind.h>
+#else
+# define VALGRIND_CREATE_MEMPOOL(_1, _2, _3) do { } while (0)
+# define VALGRIND_DESTROY_MEMPOOL(_1) do { } while (0)
+# define VALGRIND_MEMPOOL_ALLOC(_1, _2, _3) do { } while (0)
+# define VALGRIND_MEMPOOL_FREE(_1, _2) do { } while (0)
+
+/* Recent gcc will warn if you have a statement that's just a macro
+ * expanding to (0), but not if you have an inline stub function that
+ * always returns 0, so let's do the latter. */
+static inline int
+VALGRIND_MAKE_MEM_UNDEFINED (void *addr,
+ size_t len)
+{
+ return 0;
+}
+
+static inline int
+VALGRIND_PRINTF (const char *format,
+ ...)
+{
+ return 0;
+}
+
+static inline int
+VALGRIND_PRINTF_BACKTRACE (const char *format,
+ ...)
+{
+ return 0;
+}
+
+# define RUNNING_ON_VALGRIND 0
+#endif /* WITH_VALGRIND */
+
+#endif /* header guard */
diff --git a/src/3rdparty/libdbus/dbus/dbus-watch.c b/src/3rdparty/libdbus/dbus/dbus-watch.c
new file mode 100644
index 00000000..76298ba1
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-watch.c
@@ -0,0 +1,764 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-watch.c DBusWatch implementation
+ *
+ * Copyright (C) 2002, 2003 Red Hat Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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-watch.h"
+#include "dbus-list.h"
+
+/**
+ * @defgroup DBusWatchInternals DBusWatch implementation details
+ * @ingroup DBusInternals
+ * @brief implementation details for DBusWatch
+ *
+ * @{
+ */
+
+/**
+ * Implementation of DBusWatch
+ */
+struct DBusWatch
+{
+ int refcount; /**< Reference count */
+ DBusPollable fd; /**< File descriptor. */
+ unsigned int flags; /**< Conditions to watch. */
+
+ DBusWatchHandler handler; /**< Watch handler. */
+ void *handler_data; /**< Watch handler data. */
+ DBusFreeFunction free_handler_data_function; /**< Free the watch handler data. */
+
+ void *data; /**< Application data. */
+ DBusFreeFunction free_data_function; /**< Free the application data. */
+ unsigned int enabled : 1; /**< Whether it's enabled. */
+ unsigned int oom_last_time : 1; /**< Whether it was OOM last time. */
+};
+
+dbus_bool_t
+_dbus_watch_get_enabled (DBusWatch *watch)
+{
+ return watch->enabled;
+}
+
+dbus_bool_t
+_dbus_watch_get_oom_last_time (DBusWatch *watch)
+{
+ return watch->oom_last_time;
+}
+
+void
+_dbus_watch_set_oom_last_time (DBusWatch *watch,
+ dbus_bool_t oom)
+{
+ watch->oom_last_time = oom;
+}
+
+/**
+ * Creates a new DBusWatch. Used to add a file descriptor to be polled
+ * by a main loop.
+ *
+ * @param fd the file descriptor to be watched.
+ * @param flags the conditions to watch for on the descriptor.
+ * @param enabled the initial enabled state
+ * @param handler the handler function
+ * @param data data for handler function
+ * @param free_data_function function to free the data
+ * @returns the new DBusWatch object.
+ */
+DBusWatch*
+_dbus_watch_new (DBusPollable fd,
+ unsigned int flags,
+ dbus_bool_t enabled,
+ DBusWatchHandler handler,
+ void *data,
+ DBusFreeFunction free_data_function)
+{
+ DBusWatch *watch;
+
+#define VALID_WATCH_FLAGS (DBUS_WATCH_WRITABLE | DBUS_WATCH_READABLE)
+
+ _dbus_assert ((flags & VALID_WATCH_FLAGS) == flags);
+
+ watch = dbus_new0 (DBusWatch, 1);
+ if (watch == NULL)
+ return NULL;
+
+ watch->refcount = 1;
+ watch->fd = fd;
+ watch->flags = flags;
+ watch->enabled = enabled;
+
+ watch->handler = handler;
+ watch->handler_data = data;
+ watch->free_handler_data_function = free_data_function;
+
+ return watch;
+}
+
+/**
+ * Increments the reference count of a DBusWatch object.
+ *
+ * @param watch the watch object.
+ * @returns the watch object.
+ */
+DBusWatch *
+_dbus_watch_ref (DBusWatch *watch)
+{
+ watch->refcount += 1;
+
+ return watch;
+}
+
+/**
+ * Decrements the reference count of a DBusWatch object
+ * and finalizes the object if the count reaches zero.
+ *
+ * @param watch the watch object.
+ */
+void
+_dbus_watch_unref (DBusWatch *watch)
+{
+ _dbus_assert (watch != NULL);
+ _dbus_assert (watch->refcount > 0);
+
+ watch->refcount -= 1;
+ if (watch->refcount == 0)
+ {
+ if (_dbus_pollable_is_valid (watch->fd))
+ _dbus_warn ("this watch should have been invalidated");
+
+ dbus_watch_set_data (watch, NULL, NULL); /* call free_data_function */
+
+ if (watch->free_handler_data_function)
+ (* watch->free_handler_data_function) (watch->handler_data);
+
+ dbus_free (watch);
+ }
+}
+
+/**
+ * Clears the file descriptor from a now-invalid watch object so that
+ * no one tries to use it. This is because a watch may stay alive due
+ * to reference counts after the file descriptor is closed.
+ * Invalidation makes it easier to catch bugs. It also
+ * keeps people from doing dorky things like assuming file descriptors
+ * are unique (never recycled).
+ *
+ * @param watch the watch object.
+ */
+void
+_dbus_watch_invalidate (DBusWatch *watch)
+{
+ _dbus_pollable_invalidate (&watch->fd);
+ watch->flags = 0;
+}
+
+/**
+ * Sanitizes the given condition so that it only contains
+ * flags that the DBusWatch requested. e.g. if the
+ * watch is a DBUS_WATCH_READABLE watch then
+ * DBUS_WATCH_WRITABLE will be stripped from the condition.
+ *
+ * @param watch the watch object.
+ * @param condition address of the condition to sanitize.
+ */
+void
+_dbus_watch_sanitize_condition (DBusWatch *watch,
+ unsigned int *condition)
+{
+ if (!(watch->flags & DBUS_WATCH_READABLE))
+ *condition &= ~DBUS_WATCH_READABLE;
+ if (!(watch->flags & DBUS_WATCH_WRITABLE))
+ *condition &= ~DBUS_WATCH_WRITABLE;
+}
+
+
+/**
+ * @typedef DBusWatchList
+ *
+ * Opaque data type representing a list of watches
+ * and a set of DBusAddWatchFunction/DBusRemoveWatchFunction.
+ * Automatically handles removing/re-adding watches
+ * when the DBusAddWatchFunction is updated or changed.
+ * Holds a reference count to each watch.
+ *
+ * Used in the implementation of both DBusServer and
+ * DBusClient.
+ *
+ */
+
+/**
+ * DBusWatchList implementation details. All fields
+ * are private.
+ *
+ */
+struct DBusWatchList
+{
+ DBusList *watches; /**< Watch objects. */
+
+ DBusAddWatchFunction add_watch_function; /**< Callback for adding a watch. */
+ DBusRemoveWatchFunction remove_watch_function; /**< Callback for removing a watch. */
+ DBusWatchToggledFunction watch_toggled_function; /**< Callback on toggling enablement */
+ void *watch_data; /**< Data for watch callbacks */
+ DBusFreeFunction watch_free_data_function; /**< Free function for watch callback data */
+};
+
+/**
+ * Creates a new watch list. Returns #NULL if insufficient
+ * memory exists.
+ *
+ * @returns the new watch list, or #NULL on failure.
+ */
+DBusWatchList*
+_dbus_watch_list_new (void)
+{
+ DBusWatchList *watch_list;
+
+ watch_list = dbus_new0 (DBusWatchList, 1);
+ if (watch_list == NULL)
+ return NULL;
+
+ return watch_list;
+}
+
+/**
+ * Frees a DBusWatchList.
+ *
+ * @param watch_list the watch list.
+ */
+void
+_dbus_watch_list_free (DBusWatchList *watch_list)
+{
+ /* free watch_data and removes watches as a side effect */
+ _dbus_watch_list_set_functions (watch_list,
+ NULL, NULL, NULL, NULL, NULL);
+
+ _dbus_list_clear_full (&watch_list->watches,
+ (DBusFreeFunction) _dbus_watch_unref);
+
+ dbus_free (watch_list);
+}
+
+#ifdef DBUS_ENABLE_VERBOSE_MODE
+static const char*
+watch_flags_to_string (int flags)
+{
+ const char *watch_type;
+
+ if ((flags & DBUS_WATCH_READABLE) &&
+ (flags & DBUS_WATCH_WRITABLE))
+ watch_type = "readwrite";
+ else if (flags & DBUS_WATCH_READABLE)
+ watch_type = "read";
+ else if (flags & DBUS_WATCH_WRITABLE)
+ watch_type = "write";
+ else
+ watch_type = "not read or write";
+ return watch_type;
+}
+#endif /* DBUS_ENABLE_VERBOSE_MODE */
+
+/**
+ * Sets the watch functions. This function is the "backend"
+ * for dbus_connection_set_watch_functions() and
+ * dbus_server_set_watch_functions().
+ *
+ * @param watch_list the watch list.
+ * @param add_function the add watch function.
+ * @param remove_function the remove watch function.
+ * @param toggled_function function on toggling enabled flag, or #NULL
+ * @param data the data for those functions.
+ * @param free_data_function the function to free the data.
+ * @returns #FALSE if not enough memory
+ *
+ */
+dbus_bool_t
+_dbus_watch_list_set_functions (DBusWatchList *watch_list,
+ DBusAddWatchFunction add_function,
+ DBusRemoveWatchFunction remove_function,
+ DBusWatchToggledFunction toggled_function,
+ void *data,
+ DBusFreeFunction free_data_function)
+{
+ /* Add watches with the new watch function, failing on OOM */
+ if (add_function != NULL)
+ {
+ DBusList *link;
+
+ link = _dbus_list_get_first_link (&watch_list->watches);
+ while (link != NULL)
+ {
+ DBusList *next = _dbus_list_get_next_link (&watch_list->watches,
+ link);
+#ifdef DBUS_ENABLE_VERBOSE_MODE
+ DBusWatch *watch = link->data;
+
+ _dbus_verbose ("Adding a %s watch on fd %" DBUS_POLLABLE_FORMAT " using newly-set add watch function\n",
+ watch_flags_to_string (dbus_watch_get_flags (link->data)),
+ _dbus_pollable_printable (watch->fd));
+#endif
+
+ if (!(* add_function) (link->data, data))
+ {
+ /* remove it all again and return FALSE */
+ DBusList *link2;
+
+ link2 = _dbus_list_get_first_link (&watch_list->watches);
+ while (link2 != link)
+ {
+ DBusList *next2 = _dbus_list_get_next_link (&watch_list->watches,
+ link2);
+#ifdef DBUS_ENABLE_VERBOSE_MODE
+ DBusWatch *watch2 = link2->data;
+
+ _dbus_verbose ("Removing watch on fd %" DBUS_POLLABLE_FORMAT " using newly-set remove function because initial add failed\n",
+ _dbus_pollable_printable (watch2->fd));
+#endif
+
+ (* remove_function) (link2->data, data);
+
+ link2 = next2;
+ }
+
+ return FALSE;
+ }
+
+ link = next;
+ }
+ }
+
+ /* Remove all current watches from previous watch handlers */
+
+ if (watch_list->remove_watch_function != NULL)
+ {
+ _dbus_verbose ("Removing all pre-existing watches\n");
+
+ _dbus_list_foreach (&watch_list->watches,
+ (DBusForeachFunction) watch_list->remove_watch_function,
+ watch_list->watch_data);
+ }
+
+ if (watch_list->watch_free_data_function != NULL)
+ (* watch_list->watch_free_data_function) (watch_list->watch_data);
+
+ watch_list->add_watch_function = add_function;
+ watch_list->remove_watch_function = remove_function;
+ watch_list->watch_toggled_function = toggled_function;
+ watch_list->watch_data = data;
+ watch_list->watch_free_data_function = free_data_function;
+
+ return TRUE;
+}
+
+/**
+ * Adds a new watch to the watch list, invoking the
+ * application DBusAddWatchFunction if appropriate.
+ *
+ * @param watch_list the watch list.
+ * @param watch the watch to add.
+ * @returns #TRUE on success, #FALSE if no memory.
+ */
+dbus_bool_t
+_dbus_watch_list_add_watch (DBusWatchList *watch_list,
+ DBusWatch *watch)
+{
+ if (!_dbus_list_append (&watch_list->watches, watch))
+ return FALSE;
+
+ _dbus_watch_ref (watch);
+
+ if (watch_list->add_watch_function != NULL)
+ {
+ _dbus_verbose ("Adding watch on fd %" DBUS_POLLABLE_FORMAT "\n",
+ _dbus_pollable_printable (watch->fd));
+
+ if (!(* watch_list->add_watch_function) (watch,
+ watch_list->watch_data))
+ {
+ _dbus_list_remove_last (&watch_list->watches, watch);
+ _dbus_watch_unref (watch);
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+/**
+ * Removes a watch from the watch list, invoking the
+ * application's DBusRemoveWatchFunction if appropriate.
+ *
+ * @param watch_list the watch list.
+ * @param watch the watch to remove.
+ */
+void
+_dbus_watch_list_remove_watch (DBusWatchList *watch_list,
+ DBusWatch *watch)
+{
+ if (!_dbus_list_remove (&watch_list->watches, watch))
+ _dbus_assert_not_reached ("Nonexistent watch was removed");
+
+ if (watch_list->remove_watch_function != NULL)
+ {
+ _dbus_verbose ("Removing watch on fd %" DBUS_POLLABLE_FORMAT "\n",
+ _dbus_pollable_printable (watch->fd));
+
+ (* watch_list->remove_watch_function) (watch,
+ watch_list->watch_data);
+ }
+
+ _dbus_watch_unref (watch);
+}
+
+/**
+ * Sets a watch to the given enabled state, invoking the
+ * application's DBusWatchToggledFunction if appropriate.
+ *
+ * @param watch_list the watch list.
+ * @param watch the watch to toggle.
+ * @param enabled #TRUE to enable
+ */
+void
+_dbus_watch_list_toggle_watch (DBusWatchList *watch_list,
+ DBusWatch *watch,
+ dbus_bool_t enabled)
+{
+ enabled = !!enabled;
+
+ if (enabled == watch->enabled)
+ return;
+
+ watch->enabled = enabled;
+
+ if (watch_list->watch_toggled_function != NULL)
+ {
+ _dbus_verbose ("Toggling watch %p on fd %" DBUS_POLLABLE_FORMAT " to %d\n",
+ watch,
+ _dbus_pollable_printable (watch->fd),
+ watch->enabled);
+
+ (* watch_list->watch_toggled_function) (watch,
+ watch_list->watch_data);
+ }
+}
+
+/**
+ * Sets all watches to the given enabled state, invoking the
+ * application's DBusWatchToggledFunction if appropriate.
+ *
+ * @param watch_list the watch list.
+ * @param enabled #TRUE to enable
+ */
+void
+_dbus_watch_list_toggle_all_watches (DBusWatchList *watch_list,
+ dbus_bool_t enabled)
+{
+ DBusList *link;
+
+ for (link = _dbus_list_get_first_link (&watch_list->watches);
+ link != NULL;
+ link = _dbus_list_get_next_link (&watch_list->watches, link))
+ {
+ _dbus_watch_list_toggle_watch (watch_list, link->data, enabled);
+ }
+}
+
+/**
+ * Sets the handler for the watch.
+ *
+ * @todo this function only exists because of the weird
+ * way connection watches are done, see the note
+ * in docs for _dbus_connection_handle_watch().
+ *
+ * @param watch the watch
+ * @param handler the new handler
+ * @param data the data
+ * @param free_data_function free data with this
+ */
+void
+_dbus_watch_set_handler (DBusWatch *watch,
+ DBusWatchHandler handler,
+ void *data,
+ DBusFreeFunction free_data_function)
+{
+ if (watch->free_handler_data_function)
+ (* watch->free_handler_data_function) (watch->handler_data);
+
+ watch->handler = handler;
+ watch->handler_data = data;
+ watch->free_handler_data_function = free_data_function;
+}
+
+/** @} */
+
+/**
+ * @defgroup DBusWatch DBusWatch
+ * @ingroup DBus
+ * @brief Object representing a file descriptor to be watched.
+ *
+ * Types and functions related to DBusWatch. A watch represents
+ * a file descriptor that the main loop needs to monitor,
+ * as in Qt's QSocketNotifier or GLib's g_io_add_watch().
+ *
+ * Use dbus_connection_set_watch_functions() or dbus_server_set_watch_functions()
+ * to be notified when libdbus needs to add or remove watches.
+ *
+ * @{
+ */
+
+/**
+ * @typedef DBusWatch
+ *
+ * Opaque object representing a file descriptor
+ * to be watched for changes in readability,
+ * writability, or hangup.
+ */
+
+/**
+ * Deprecated former name of dbus_watch_get_unix_fd().
+ *
+ * @param watch the DBusWatch object.
+ * @returns the file descriptor to watch.
+ */
+int
+dbus_watch_get_fd (DBusWatch *watch)
+{
+ _dbus_return_val_if_fail (watch != NULL, -1);
+
+ return dbus_watch_get_unix_fd(watch);
+}
+
+/**
+ * Returns a UNIX file descriptor to be watched,
+ * which may be a pipe, socket, or other type of
+ * descriptor. On UNIX this is preferred to
+ * dbus_watch_get_socket() since it works with
+ * more kinds of #DBusWatch.
+ *
+ * Always returns -1 on Windows. On Windows you use
+ * dbus_watch_get_socket() to get a Winsock socket to watch.
+ *
+ * @param watch the DBusWatch object.
+ * @returns the file descriptor to watch.
+ */
+int
+dbus_watch_get_unix_fd (DBusWatch *watch)
+{
+ _dbus_return_val_if_fail (watch != NULL, -1);
+
+ /* FIXME remove #ifdef and do this on a lower level
+ * (watch should have set_socket and set_unix_fd and track
+ * which it has, and the transport should provide the
+ * appropriate watch type)
+ */
+#ifdef DBUS_UNIX
+ return watch->fd;
+#else
+ return dbus_watch_get_socket( watch );
+#endif
+}
+
+/**
+ * Returns a socket to be watched, on UNIX this will return -1 if our
+ * transport is not socket-based so dbus_watch_get_unix_fd() is
+ * preferred.
+ *
+ * On Windows, dbus_watch_get_unix_fd() returns -1 but this function
+ * returns a Winsock socket (assuming the transport is socket-based,
+ * as it always is for now).
+ *
+ * @param watch the DBusWatch object.
+ * @returns the socket to watch.
+ */
+int
+dbus_watch_get_socket (DBusWatch *watch)
+{
+ _dbus_return_val_if_fail (watch != NULL, -1);
+
+#ifdef DBUS_UNIX
+ return watch->fd;
+#else
+ return _dbus_socket_get_int (watch->fd);
+#endif
+}
+
+DBusSocket
+_dbus_watch_get_socket (DBusWatch *watch)
+{
+ DBusSocket s;
+
+ _dbus_assert (watch != NULL);
+
+#ifdef DBUS_UNIX
+ s.fd = watch->fd;
+#else
+ s = watch->fd;
+#endif
+
+ return s;
+}
+
+DBusPollable
+_dbus_watch_get_pollable (DBusWatch *watch)
+{
+ _dbus_assert (watch != NULL);
+
+ return watch->fd;
+}
+
+/**
+ * Gets flags from DBusWatchFlags indicating
+ * what conditions should be monitored on the
+ * file descriptor.
+ *
+ * The flags returned will only contain DBUS_WATCH_READABLE
+ * and DBUS_WATCH_WRITABLE, never DBUS_WATCH_HANGUP or
+ * DBUS_WATCH_ERROR; all watches implicitly include a watch
+ * for hangups, errors, and other exceptional conditions.
+ *
+ * @param watch the DBusWatch object.
+ * @returns the conditions to watch.
+ */
+unsigned int
+dbus_watch_get_flags (DBusWatch *watch)
+{
+ _dbus_return_val_if_fail (watch != NULL, 0);
+ _dbus_assert ((watch->flags & VALID_WATCH_FLAGS) == watch->flags);
+
+ return watch->flags;
+}
+
+/**
+ * Gets data previously set with dbus_watch_set_data()
+ * or #NULL if none.
+ *
+ * @param watch the DBusWatch object.
+ * @returns previously-set data.
+ */
+void*
+dbus_watch_get_data (DBusWatch *watch)
+{
+ _dbus_return_val_if_fail (watch != NULL, NULL);
+
+ return watch->data;
+}
+
+/**
+ * Sets data which can be retrieved with dbus_watch_get_data().
+ * Intended for use by the DBusAddWatchFunction and
+ * DBusRemoveWatchFunction to store their own data. For example with
+ * Qt you might store the QSocketNotifier for this watch and with GLib
+ * you might store a GSource.
+ *
+ * @param watch the DBusWatch object.
+ * @param data the data.
+ * @param free_data_function function to be called to free the data.
+ */
+void
+dbus_watch_set_data (DBusWatch *watch,
+ void *data,
+ DBusFreeFunction free_data_function)
+{
+ _dbus_return_if_fail (watch != NULL);
+
+ _dbus_verbose ("Setting watch fd %" DBUS_POLLABLE_FORMAT " data to data = %p function = %p from data = %p function = %p\n",
+ _dbus_pollable_printable (watch->fd),
+ data, free_data_function, watch->data, watch->free_data_function);
+
+ if (watch->free_data_function != NULL)
+ (* watch->free_data_function) (watch->data);
+
+ watch->data = data;
+ watch->free_data_function = free_data_function;
+}
+
+/**
+ * Returns whether a watch is enabled or not. If not
+ * enabled, it should not be polled by the main loop.
+ *
+ * @param watch the DBusWatch object
+ * @returns #TRUE if the watch is enabled
+ */
+dbus_bool_t
+dbus_watch_get_enabled (DBusWatch *watch)
+{
+ _dbus_return_val_if_fail (watch != NULL, FALSE);
+
+ return watch->enabled;
+}
+
+
+/**
+ * Called to notify the D-Bus library when a previously-added watch is
+ * ready for reading or writing, or has an exception such as a hangup.
+ *
+ * If this function returns #FALSE, then the file descriptor may still
+ * be ready for reading or writing, but more memory is needed in order
+ * to do the reading or writing. If you ignore the #FALSE return, your
+ * application may spin in a busy loop on the file descriptor until
+ * memory becomes available, but nothing more catastrophic should
+ * happen.
+ *
+ * dbus_watch_handle() cannot be called during the
+ * DBusAddWatchFunction, as the connection will not be ready to handle
+ * that watch yet.
+ *
+ * It is not allowed to reference a DBusWatch after it has been passed
+ * to remove_function.
+ *
+ * @param watch the DBusWatch object.
+ * @param flags the poll condition using #DBusWatchFlags values
+ * @returns #FALSE if there wasn't enough memory
+ */
+dbus_bool_t
+dbus_watch_handle (DBusWatch *watch,
+ unsigned int flags)
+{
+ _dbus_return_val_if_fail (watch != NULL, FALSE);
+
+#ifndef DBUS_DISABLE_CHECKS
+ if (!_dbus_pollable_is_valid (watch->fd) || watch->flags == 0)
+ {
+ _dbus_warn_check_failed ("Watch is invalid, it should have been removed");
+ return TRUE;
+ }
+#endif
+
+ _dbus_return_val_if_fail (_dbus_pollable_is_valid (watch->fd) /* fails if watch was removed */, TRUE);
+
+ _dbus_watch_sanitize_condition (watch, &flags);
+
+ if (flags == 0)
+ {
+ _dbus_verbose ("After sanitization, watch flags on fd %" DBUS_POLLABLE_FORMAT " were 0\n",
+ _dbus_pollable_printable (watch->fd));
+ return TRUE;
+ }
+ else
+ return (* watch->handler) (watch, flags,
+ watch->handler_data);
+}
+
+
+/** @} */
diff --git a/src/3rdparty/libdbus/dbus/dbus-watch.h b/src/3rdparty/libdbus/dbus/dbus-watch.h
new file mode 100644
index 00000000..2b278461
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus-watch.h
@@ -0,0 +1,115 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-watch.h DBusWatch internal interfaces
+ *
+ * Copyright (C) 2002 Red Hat Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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
+ *
+ */
+#ifndef DBUS_WATCH_H
+#define DBUS_WATCH_H
+
+#include <dbus/dbus-internals.h>
+#include <dbus/dbus-connection.h>
+
+DBUS_BEGIN_DECLS
+
+/**
+ * @addtogroup DBusWatchInternals
+ * @{
+ */
+
+/* Public methods on DBusWatch are in dbus-connection.h */
+
+typedef struct DBusWatchList DBusWatchList;
+
+#define _DBUS_WATCH_NVAL (1<<4)
+
+/** function to run when the watch is handled */
+typedef dbus_bool_t (* DBusWatchHandler) (DBusWatch *watch,
+ unsigned int flags,
+ void *data);
+
+DBUS_PRIVATE_EXPORT
+DBusWatch* _dbus_watch_new (DBusPollable fd,
+ unsigned int flags,
+ dbus_bool_t enabled,
+ DBusWatchHandler handler,
+ void *data,
+ DBusFreeFunction free_data_function);
+DBUS_PRIVATE_EXPORT
+DBusWatch* _dbus_watch_ref (DBusWatch *watch);
+DBUS_PRIVATE_EXPORT
+void _dbus_watch_unref (DBusWatch *watch);
+DBUS_PRIVATE_EXPORT
+void _dbus_watch_invalidate (DBusWatch *watch);
+void _dbus_watch_sanitize_condition (DBusWatch *watch,
+ unsigned int *condition);
+void _dbus_watch_set_handler (DBusWatch *watch,
+ DBusWatchHandler handler,
+ void *data,
+ DBusFreeFunction free_data_function);
+
+
+DBUS_PRIVATE_EXPORT
+DBusWatchList* _dbus_watch_list_new (void);
+DBUS_PRIVATE_EXPORT
+void _dbus_watch_list_free (DBusWatchList *watch_list);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_watch_list_set_functions (DBusWatchList *watch_list,
+ DBusAddWatchFunction add_function,
+ DBusRemoveWatchFunction remove_function,
+ DBusWatchToggledFunction toggled_function,
+ void *data,
+ DBusFreeFunction free_data_function);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_watch_list_add_watch (DBusWatchList *watch_list,
+ DBusWatch *watch);
+DBUS_PRIVATE_EXPORT
+void _dbus_watch_list_remove_watch (DBusWatchList *watch_list,
+ DBusWatch *watch);
+void _dbus_watch_list_toggle_watch (DBusWatchList *watch_list,
+ DBusWatch *watch,
+ dbus_bool_t enabled);
+void _dbus_watch_list_toggle_all_watches (DBusWatchList *watch_list,
+ dbus_bool_t enabled);
+dbus_bool_t _dbus_watch_get_enabled (DBusWatch *watch);
+
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_watch_get_oom_last_time (DBusWatch *watch);
+DBUS_PRIVATE_EXPORT
+void _dbus_watch_set_oom_last_time (DBusWatch *watch,
+ dbus_bool_t oom);
+
+DBusSocket _dbus_watch_get_socket (DBusWatch *watch);
+DBUS_PRIVATE_EXPORT
+DBusPollable _dbus_watch_get_pollable (DBusWatch *watch);
+
+static inline void
+_dbus_clear_watch (DBusWatch **pointer_to_watch)
+{
+ _dbus_clear_pointer_impl (DBusWatch, pointer_to_watch,
+ _dbus_watch_unref);
+}
+
+/** @} */
+
+DBUS_END_DECLS
+
+#endif /* DBUS_WATCH_H */
diff --git a/src/3rdparty/libdbus/dbus/dbus.h b/src/3rdparty/libdbus/dbus/dbus.h
new file mode 100644
index 00000000..53998c54
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus/dbus.h
@@ -0,0 +1,106 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus.h Convenience header including all other headers
+ *
+ * Copyright (C) 2002, 2003 Red Hat Inc.
+ *
+ * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+ *
+ * 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
+ *
+ */
+
+#ifndef DBUS_H
+#define DBUS_H
+
+#define DBUS_INSIDE_DBUS_H 1
+
+#include <dbus/dbus-arch-deps.h>
+#include <dbus/dbus-address.h>
+#include <dbus/dbus-bus.h>
+#include <dbus/dbus-connection.h>
+#include <dbus/dbus-errors.h>
+#include <dbus/dbus-macros.h>
+#include <dbus/dbus-message.h>
+#include <dbus/dbus-misc.h>
+#include <dbus/dbus-pending-call.h>
+#include <dbus/dbus-protocol.h>
+#include <dbus/dbus-server.h>
+#include <dbus/dbus-shared.h>
+#include <dbus/dbus-signature.h>
+#include <dbus/dbus-syntax.h>
+#include <dbus/dbus-threads.h>
+#include <dbus/dbus-types.h>
+
+#undef DBUS_INSIDE_DBUS_H
+
+/**
+ * @defgroup DBus D-Bus low-level public API
+ * @brief The low-level public API of the D-Bus library
+ *
+ * libdbus provides a low-level C API intended primarily for use by
+ * bindings to specific object systems and languages. D-Bus is most
+ * convenient when used with the GLib bindings, Python bindings, Qt
+ * bindings, Mono bindings, and so forth. This low-level API has a
+ * lot of complexity useful only for bindings.
+ *
+ * @{
+ */
+
+/** @} */
+
+/**
+ * @mainpage
+ *
+ * This manual documents the <em>low-level</em> D-Bus C API. <b>If you use
+ * this low-level API directly, you're signing up for some pain.</b>
+ *
+ * Caveats aside, you might get started learning the low-level API by reading
+ * about @ref DBusConnection and @ref DBusMessage.
+ *
+ * There are several other places to look for D-Bus information, such
+ * as the tutorial and the specification; those can be found at <a
+ * href="http://www.freedesktop.org/wiki/Software/dbus">the D-Bus
+ * website</a>. If you're interested in a sysadmin or package
+ * maintainer's perspective on the dbus-daemon itself and its
+ * configuration, be sure to check out the man pages as well.
+ *
+ * The low-level API documented in this manual deliberately lacks
+ * most convenience functions - those are left up to higher-level libraries
+ * based on frameworks such as GLib, Qt, Python, Mono, Java,
+ * etc. These higher-level libraries (often called "D-Bus bindings")
+ * have features such as object systems and main loops that allow a
+ * <em>much</em> more convenient API.
+ *
+ * The low-level API also contains plenty of clutter to support
+ * integration with arbitrary object systems, languages, main loops,
+ * and so forth. These features add a lot of noise to the API that you
+ * probably don't care about unless you're coding a binding.
+ *
+ * This manual also contains docs for @ref DBusInternals "D-Bus internals",
+ * so you can use it to get oriented to the D-Bus source code if you're
+ * interested in patching the code. You should also read the
+ * file CONTRIBUTING.md which comes with the source code if you plan to
+ * contribute to D-Bus.
+ *
+ * As you read the code, you can identify internal D-Bus functions
+ * because they start with an underscore ('_') character. Also, any
+ * identifier or macro that lacks a DBus, dbus_, or DBUS_ namepace
+ * prefix is internal, with a couple of exceptions such as #NULL,
+ * #TRUE, and #FALSE.
+ */
+
+#endif /* DBUS_H */