summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRobert Griebl <robert.griebl@qt.io>2024-02-07 02:42:37 +0100
committerRobert Griebl <robert.griebl@qt.io>2024-03-05 18:43:20 +0000
commitea49063b02733e3ec8caaf39a610fb70d24d5b0a (patch)
treefdf8f60f437789a5f2889c49cde414e776365e83
parent9c19aa9a3ae7d1eb60620e912f4c2a26cfb2b5a6 (diff)
Add libdbus to 3rdparty for Windows/macOS
On startup, we check if a libdbus-1 is provided by the system. If not we try to load our build instead, which will then be picked up by QtDBus later on. This enables us to use appman-controller in the QtCreator integration even on Windows and macOS. Change-Id: Ib832198ffd9c9e08e14d3c35cdcb4dff17f3b656 Pick-to: 6.7 Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Dominik Holland <dominik.holland@qt.io>
-rw-r--r--doc/installation.qdoc23
-rw-r--r--src/3rdparty/CMakeLists.txt4
-rw-r--r--src/3rdparty/libdbus/CMakeLists.txt164
-rw-r--r--src/3rdparty/libdbus/COPYING23
-rw-r--r--src/3rdparty/libdbus/LICENSES/AFL-2.1.txt45
-rw-r--r--src/3rdparty/libdbus/LICENSES/GPL-2.0-or-later.txt117
-rw-r--r--src/3rdparty/libdbus/config-macos.h270
-rw-r--r--src/3rdparty/libdbus/config-windows.h270
-rw-r--r--src/3rdparty/libdbus/config.h5
-rw-r--r--src/3rdparty/libdbus/dbus-arch-deps-macos.h65
-rw-r--r--src/3rdparty/libdbus/dbus-arch-deps-windows.h65
-rw-r--r--src/3rdparty/libdbus/dbus-server-launchd.c214
-rw-r--r--src/3rdparty/libdbus/dbus-server-launchd.h37
-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
-rw-r--r--src/3rdparty/libdbus/qt_attribution.json17
-rw-r--r--src/common-lib/configure.cmake9
-rw-r--r--src/common-lib/dbus-utilities.cpp60
-rw-r--r--src/common-lib/dbus-utilities.h2
-rw-r--r--src/common-lib/qt_cmdline.cmake1
-rw-r--r--src/shared-main-lib/sharedmain.cpp6
-rw-r--r--src/tools/controller/controller.cpp2
142 files changed, 74215 insertions, 6 deletions
diff --git a/doc/installation.qdoc b/doc/installation.qdoc
index 7ec89861..2693a926 100644
--- a/doc/installation.qdoc
+++ b/doc/installation.qdoc
@@ -41,8 +41,6 @@ To build the application manager with all its features, the following components
\list
\li \b Qt 6.5.0 or higher.
- \li \b openssl \e{- Linux only and only if you need the installer functionality}.
- Qt needs to be built with openssl support. The required minimum version is defined by Qt.
\li \b libyaml 2.2 or higher.
\li \b libarchive 3.5 or higher \e{- if you need the installer functionality}.
\endlist
@@ -58,6 +56,20 @@ folder are automatically used instead. Make sure you are aware of the licensing
since these bundled 3rdparty libs will be linked in as static libraries.
This option is not meant for production, but for development and testing environments only.
+\section1 Runtime Requirements
+
+Some libraries used by the application manager are not directly linked in, but instead loaded
+dynamically at runtime:
+
+\list
+ \li \b openssl \e{- Linux only and only if you need the installer functionality}.
+ Qt needs to be built with openssl support. The required minimum version is defined by Qt.
+ \li \b libdbus 1.12 or higher \e{- if you need multi-process mode or appman-controller support}.
+ As libdbus is not readily available for Windows and macOS, the application manager provides
+ its own copy to support the \c appman-controller tool (which is also used by the Qt-Creator
+ integration).
+\endlist
+
\section1 Multi-process vs. Single-process
By default, the application manager always tries to build in multi-process mode, but falls back
@@ -169,6 +181,13 @@ modules:
This gives you readable backtraces for crashes on Windows, but also increases the binary size
slightly. For debug builds, this option is enabled by default.
Settings this to \c no disables building and linking against \c stackwalker.
+\row
+ \li \c{libdbus}
+ \li Windows and macOS only: If set to \c no, enables building \c libdbus-1 from the 3rdparty
+ folder.
+ This will allow you to use the \c appman-controller tool (which is also used by the Qt-Creator
+ integration) on Windows and macOS. This option is enabled by default.
+ Settings this to \c no disables building \c libdbus-1.
\endtable
\section2 The Hardware ID
diff --git a/src/3rdparty/CMakeLists.txt b/src/3rdparty/CMakeLists.txt
index 6d0e4e17..7cea3584 100644
--- a/src/3rdparty/CMakeLists.txt
+++ b/src/3rdparty/CMakeLists.txt
@@ -7,6 +7,10 @@ if(NOT QT_FEATURE_am_system_libyaml)
add_subdirectory(libyaml)
endif()
+if(QT_FEATURE_am_libdbus)
+ add_subdirectory(libdbus)
+endif()
+
if(QT_FEATURE_am_libbacktrace)
add_subdirectory(libbacktrace)
endif()
diff --git a/src/3rdparty/libdbus/CMakeLists.txt b/src/3rdparty/libdbus/CMakeLists.txt
new file mode 100644
index 00000000..bd59b056
--- /dev/null
+++ b/src/3rdparty/libdbus/CMakeLists.txt
@@ -0,0 +1,164 @@
+
+qt_internal_add_common_qt_library_helper(BundledLibDBus SHARED)
+
+qt_internal_extend_target(BundledLibDBus
+ SOURCES
+ dbus/dbus.h
+ dbus/dbus-auth.h
+ dbus/dbus-auth.c
+ dbus/dbus-address.h
+ dbus/dbus-address.c
+ dbus/dbus-bus.h
+ dbus/dbus-bus.c
+ dbus/dbus-connection-internal.h
+ dbus/dbus-connection.h
+ dbus/dbus-connection.c
+ dbus/dbus-credentials.h
+ dbus/dbus-credentials.c
+ dbus/dbus-dataslot.h
+ dbus/dbus-dataslot.c
+ dbus/dbus-errors.h
+ dbus/dbus-errors.c
+ dbus/dbus-file.h
+ dbus/dbus-file.c
+ dbus/dbus-hash.h
+ dbus/dbus-hash.c
+ dbus/dbus-internals.h
+ dbus/dbus-internals.c
+ dbus/dbus-keyring.h
+ dbus/dbus-keyring.c
+ dbus/dbus-list.h
+ dbus/dbus-list.c
+ dbus/dbus-macros-internal.h
+ dbus/dbus-macros.h
+ dbus/dbus-marshal-basic.h
+ dbus/dbus-marshal-basic.c
+ dbus/dbus-marshal-header.h
+ dbus/dbus-marshal-header.c
+ dbus/dbus-marshal-byteswap.h
+ dbus/dbus-marshal-byteswap.c
+ dbus/dbus-marshal-recursive.h
+ dbus/dbus-marshal-recursive.c
+ dbus/dbus-marshal-validate.h
+ dbus/dbus-marshal-validate.c
+ dbus/dbus-mempool.h
+ dbus/dbus-mempool.c
+ dbus/dbus-memory.h
+ dbus/dbus-memory.c
+ dbus/dbus-message-internal.h
+ dbus/dbus-message-private.h
+ dbus/dbus-message.h
+ dbus/dbus-message.c
+ dbus/dbus-misc.h
+ dbus/dbus-misc.c
+ dbus/dbus-nonce.c
+ dbus/dbus-object-tree.h
+ dbus/dbus-object-tree.c
+ dbus/dbus-pending-call.h
+ dbus/dbus-pending-call.c
+ dbus/dbus-pipe.h
+ dbus/dbus-pipe.c
+ dbus/dbus-protocol.h
+ dbus/dbus-resources.h
+ dbus/dbus-resources.c
+ dbus/dbus-server.h
+ dbus/dbus-server.c
+ dbus/dbus-server-protected.h
+ dbus/dbus-server-socket.c
+ dbus/dbus-server-debug-pipe.h
+ dbus/dbus-server-debug-pipe.c
+ dbus/dbus-sha.h
+ dbus/dbus-sha.c
+ dbus/dbus-shared.h
+ dbus/dbus-signature.h
+ dbus/dbus-signature.c
+ dbus/dbus-string-private.h
+ dbus/dbus-string.h
+ dbus/dbus-string.c
+ dbus/dbus-syntax.h
+ dbus/dbus-syntax.c
+ dbus/dbus-sysdeps.h
+ dbus/dbus-sysdeps.c
+ dbus/dbus-timeout.h
+ dbus/dbus-timeout.c
+ dbus/dbus-threads-internal.h
+ dbus/dbus-threads.h
+ dbus/dbus-threads.c
+ dbus/dbus-transport.h
+ dbus/dbus-transport-protected.h
+ dbus/dbus-transport.c
+ dbus/dbus-transport-socket.c
+ dbus/dbus-types.h
+ dbus/dbus-uuidgen.h
+ dbus/dbus-watch.h
+ dbus/dbus-watch.c
+ DEFINES
+ DBUS_COMPILATION
+ HAVE_CONFIG_H
+ dbus_1_EXPORTS
+ INCLUDE_DIRECTORIES
+ dbus .
+)
+
+qt_internal_extend_target(BundledLibDBus CONDITION WIN32
+ SOURCES
+ dbus/dbus-backtrace-win.c
+ dbus/dbus-file-win.c
+ dbus/dbus-init-win.h
+ dbus/dbus-init-win.cpp
+ dbus/dbus-pipe-win.c
+ dbus/dbus-server-win.c
+ dbus/dbus-sysdeps-win.h
+ dbus/dbus-sysdeps-win.c
+ dbus/dbus-sysdeps-thread-win.c
+ dbus/dbus-sockets-win.h
+ dbus/dbus-transport-win.h
+ dbus/dbus-transport-win.c
+ DEFINES
+ _CRT_NONSTDC_NO_DEPRECATE
+ _CRT_SECURE_NO_DEPRECATE
+ PLATFORM_CONFIG_H="config-windows.h"
+ PLATFORM_DBUS_ARCH_DEPS_H="../dbus-arch-deps-windows.h"
+ PUBLIC_LIBRARIES
+ ws2_32 advapi32 netapi32 iphlpapi dbghelp
+)
+
+qt_internal_extend_target(BundledLibDBus CONDITION MACOS
+ SOURCES
+ dbus/dbus-file-unix.c
+ dbus/dbus-pipe-unix.c
+ dbus-server-launchd.h
+ dbus-server-launchd.c
+ dbus/dbus-server-unix.c
+ dbus/dbus-sysdeps-unix.h
+ dbus/dbus-sysdeps-unix.c
+ dbus/dbus-sysdeps-pthread.c
+ dbus/dbus-transport-unix.h
+ dbus/dbus-transport-unix.h
+ dbus/dbus-transport-unix.c
+ dbus/dbus-userdb.h
+ dbus/dbus-userdb.c
+ dbus/dbus-uuidgen.c
+ DEFINES
+ PLATFORM_CONFIG_H="config-macos.h"
+ PLATFORM_DBUS_ARCH_DEPS_H="../dbus-arch-deps-macos.h"
+)
+
+qt_disable_warnings(BundledLibDBus)
+set_target_properties(BundledLibDBus PROPERTIES
+ OUTPUT_NAME "dbus-1"
+ DEBUG_POSTFIX "" # cmake should be using this for dylibs...
+ FRAMEWORK_MULTI_CONFIG_POSTFIX_DEBUG "" # ...but is using that instead
+ LIBRARY_OUTPUT_DIRECTORY "${QT_BUILD_DIR}/${INSTALL_LIBDIR}/qtapplicationmanager"
+ RUNTIME_OUTPUT_DIRECTORY "${QT_BUILD_DIR}/${INSTALL_BINDIR}/qtapplicationmanager"
+ ARCHIVE_OUTPUT_DIRECTORY "${QT_BUILD_DIR}/${INSTALL_LIBDIR}/qtapplicationmanager"
+ VERSION 3
+ SOVERSION 3
+ _qt_module_skip_depends_include TRUE
+)
+
+qt_install(TARGETS BundledLibDBus
+ RUNTIME DESTINATION "${INSTALL_BINDIR}/qtapplicationmanager"
+ LIBRARY DESTINATION "${INSTALL_LIBDIR}/qtapplicationmanager"
+ ARCHIVE DESTINATION "${INSTALL_LIBDIR}/qtapplicationmanager"
+)
diff --git a/src/3rdparty/libdbus/COPYING b/src/3rdparty/libdbus/COPYING
new file mode 100644
index 00000000..d0afa71b
--- /dev/null
+++ b/src/3rdparty/libdbus/COPYING
@@ -0,0 +1,23 @@
+dbus is licensed to you under your choice of the Academic Free
+License version 2.1, or the GNU General Public License version 2
+(or, at your option any later version).
+The full text of these licenses can be found in LICENSES/AFL-2.1.txt
+and LICENSES/GPL-2.0-or-later.txt.
+
+Some source files are under more permissive BSD-/MIT-style licenses
+that are compatible with the AFL and GPL.
+
+When contributing new code, our preferred license for new code is the
+version of the MIT/X11 license used by the Expat library, referred to
+as "MIT" by SPDX, which can be found in LICENSES/MIT.txt.
+
+Some of the standalone binaries are under the GPL only; in particular,
+but not limited to, tools/dbus-cleanup-sockets.c and test/decode-gcov.c.
+
+Each source code file is marked with the proper copyright information -
+if you find a file that isn't marked please bring it to our attention.
+
+Some files contain a SPDX-License-Identifier marker. These markers
+indicate the license of that file, consistent with the SPDX and REUSE
+specifications: see <https://reuse.software/>. dbus is not yet fully
+REUSE-compliant, and not all files carry these markers.
diff --git a/src/3rdparty/libdbus/LICENSES/AFL-2.1.txt b/src/3rdparty/libdbus/LICENSES/AFL-2.1.txt
new file mode 100644
index 00000000..011d6d48
--- /dev/null
+++ b/src/3rdparty/libdbus/LICENSES/AFL-2.1.txt
@@ -0,0 +1,45 @@
+The Academic Free License
+v.2.1
+
+This Academic Free License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following notice immediately following the copyright notice for the Original Work:
+
+ Licensed under the Academic Free License version 2.1
+
+1) Grant of Copyright License. Licensor hereby grants You a world-wide, royalty-free, non-exclusive, perpetual, sublicenseable license to do the following:
+
+ a) to reproduce the Original Work in copies;
+ b) to prepare derivative works ("Derivative Works") based upon the Original Work;
+ c) to distribute copies of the Original Work and Derivative Works to the public;
+ d) to perform the Original Work publicly; and
+ e) to display the Original Work publicly.
+
+2) Grant of Patent License. Licensor hereby grants You a world-wide, royalty-free, non-exclusive, perpetual, sublicenseable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, to make, use, sell and offer for sale the Original Work and Derivative Works.
+
+3) Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor hereby agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work, and by publishing the address of that information repository in a notice immediately following the copyright notice that applies to the Original Work.
+
+4) Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior written permission of the Licensor. Nothing in this License shall be deemed to grant any rights to trademarks, copyrights, patents, trade secrets or any other intellectual property of Licensor except as expressly stated herein. No patent license is granted to make, use, sell or offer to sell embodiments of any patent claims other than the licensed claims defined in Section 2. No right is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under different terms from this License any Original Work that Licensor otherwise would have a right to license.
+
+5) This section intentionally omitted.
+
+6) Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work.
+
+7) Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately proceeding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of NON-INFRINGEMENT, MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to Original Work is granted hereunder except under this disclaimer.
+
+8) Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to any person for any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to liability for death or personal injury resulting from Licensor's negligence to the extent applicable law prohibits such limitation. Some jurisdictions do not allow the exclusion or limitation of incidental or consequential damages, so this exclusion and limitation may not apply to You.
+
+ 9) Acceptance and Termination. If You distribute copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. Nothing else but this License (or another written agreement between Licensor and You) grants You permission to create Derivative Works based upon the Original Work or to exercise any of the rights granted in Section 1 herein, and any attempt to do so except under the terms of this License (or another written agreement between Licensor and You) is expressly prohibited by U.S. copyright law, the equivalent laws of other countries, and by international treaty. Therefore, by exercising any of the rights granted to You in Section 1 herein, You indicate Your acceptance of this License and all of its terms and conditions.
+
+10) Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware.
+
+11) Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of the U.S. Copyright Act, 17 U.S.C. § 101 et seq., the equivalent laws of other countries, and international treaty. This section shall survive the termination of this License.
+
+12) Attorneys Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License.
+
+13) Miscellaneous. This License represents the complete agreement concerning the subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable.
+
+14) Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
+
+15) Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You.
+
+This license is Copyright (C) 2003-2004 Lawrence E. Rosen. All rights reserved.
+Permission is hereby granted to copy and distribute this license without modification. This license may not be modified without the express written permission of its copyright owner.
diff --git a/src/3rdparty/libdbus/LICENSES/GPL-2.0-or-later.txt b/src/3rdparty/libdbus/LICENSES/GPL-2.0-or-later.txt
new file mode 100644
index 00000000..17cb2864
--- /dev/null
+++ b/src/3rdparty/libdbus/LICENSES/GPL-2.0-or-later.txt
@@ -0,0 +1,117 @@
+GNU GENERAL PUBLIC LICENSE
+Version 2, June 1991
+
+Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+
+Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
+
+Preamble
+
+The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too.
+
+When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things.
+
+To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights.
+
+We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software.
+
+Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations.
+
+Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all.
+
+The precise terms and conditions for copying, distribution and modification follow.
+
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does.
+
+1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee.
+
+2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License.
+
+3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable.
+
+If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code.
+
+4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.
+
+5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it.
+
+6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License.
+
+7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice.
+
+This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License.
+
+8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License.
+
+9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation.
+
+10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally.
+
+NO WARRANTY
+
+11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+END OF TERMS AND CONDITIONS
+
+How to Apply These Terms to Your New Programs
+
+If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms.
+
+To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found.
+
+ one line to give the program's name and an idea of what it does. Copyright (C) yyyy name of author
+
+ 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. Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+signature of Ty Coon, 1 April 1989 Ty Coon, President of Vice
diff --git a/src/3rdparty/libdbus/config-macos.h b/src/3rdparty/libdbus/config-macos.h
new file mode 100644
index 00000000..2e357e28
--- /dev/null
+++ b/src/3rdparty/libdbus/config-macos.h
@@ -0,0 +1,270 @@
+/* config.h. Generated by cmake from config.h.cmake */
+
+#ifndef _DBUS_CONFIG_H
+#define _DBUS_CONFIG_H
+
+#define VERSION "1.15.9"
+
+/* On Windows, we expect to be using msvcrt.dll-compatible printf
+ * (%I64u instead of %llu) unless otherwise specified. This must be
+ * done near the beginning of config.h, before we have included any
+ * system headers that might check the value of this macro. */
+#ifndef __USE_MINGW_ANSI_STDIO
+# define __USE_MINGW_ANSI_STDIO 0
+#endif
+
+/* #undef _FILE_OFFSET_BITS */
+/* #undef _TIME_BITS */
+/* #undef WORDS_BIGENDIAN */
+
+/* Opt-in to modern APIs and thread-safety for Solaris. In the Autotools
+ * build system we do the equivalent of this by appending to CFLAGS
+ * in configure.ac */
+#ifdef __sun
+# define __EXTENSIONS__
+# define _POSIX_PTHREAD_SEMANTICS
+# define _REENTRANT
+# define _XOPEN_SOURCE 500
+#endif
+
+#define DBUS_DATADIR "/usr/local/share"
+#define DBUS_BINDIR "/usr/local/bin"
+#define DBUS_PREFIX "/usr/local"
+#define DBUS_SYSTEM_CONFIG_FILE "/usr/local/share/dbus-1/system.conf"
+#define DBUS_SESSION_CONFIG_FILE "/usr/local/share/dbus-1/session.conf"
+#define DBUS_SESSION_SOCKET_DIR "/var/folders/2m/j2m3v0q55bn3k4dl_pkms4k80000gn/T/"
+#define DBUS_DAEMON_NAME "dbus-daemon"
+#define DBUS_SYSTEM_BUS_DEFAULT_ADDRESS "unix:path=/usr/local/var/run/dbus/system_bus_socket"
+#define DBUS_SESSION_BUS_CONNECT_ADDRESS "autolaunch:"
+#define DBUS_MACHINE_UUID_FILE "/usr/local/var/lib/dbus/machine-id"
+#define DBUS_DAEMONDIR "/usr/local/bin"
+#define DBUS_RUNSTATEDIR "/usr/local/var/run"
+
+/* #undef DBUS_ENABLE_STATS */
+#define ENABLE_TRADITIONAL_ACTIVATION
+
+#define TEST_LISTEN ""
+
+// test binaries
+#define DBUS_EXEEXT ""
+
+/* Some dbus features */
+/* #undef DBUS_ENABLE_ANSI */
+#define DBUS_ENABLE_VERBOSE_MODE 1
+/* #undef DBUS_DISABLE_ASSERT */
+#ifndef DBUS_DISABLE_ASSERT
+# define DBUS_ENABLE_ASSERT 1
+#endif
+/* #undef DBUS_DISABLE_CHECKS */
+#ifndef DBUS_DISABLE_CHECKS
+# define DBUS_ENABLE_CHECKS 1
+#endif
+/* #undef DBUS_ENABLE_EMBEDDED_TESTS */
+/* #undef DBUS_ENABLE_MODULAR_TESTS */
+/* #undef DBUS_USE_OUTPUT_DEBUG_STRING */
+
+/* xmldocs */
+/* doxygen */
+/* #undef DBUS_GCOV_ENABLED */
+
+/* selinux */
+/* kqueue */
+/* #undef HAVE_CONSOLE_OWNER_FILE */
+#define DBUS_CONSOLE_OWNER_FILE ""
+
+/* #undef DBUS_BUILD_X11 */
+/* For the moment, the cmake build system doesn't have an equivalent of
+ * the autoconf build system's --disable-x11-autolaunch */
+#ifdef DBUS_BUILD_X11
+# define DBUS_ENABLE_X11_AUTOLAUNCH 1
+#endif
+
+/* #undef DBUS_WITH_GLIB */
+/* #undef GLIB_VERSION_MIN_REQUIRED */
+/* #undef GLIB_VERSION_MAX_ALLOWED */
+
+// headers
+/* #undef HAVE_AFUNIX_H */
+#define HAVE_ALLOCA_H 1
+/* #undef HAVE_BYTESWAP_H */
+/* #undef HAVE_CRT_EXTERNS_H */
+
+/* Define to 1 if you have dirent.h */
+#define HAVE_DIRENT_H 1
+
+/* Define to 1 if you have errno.h */
+#define HAVE_ERRNO_H 1
+
+/* Define to 1 if you have inttypes.h */
+#define HAVE_INTTYPES_H 1
+
+/* Define to 1 if you have io.h */
+/* #undef HAVE_IO_H */
+
+/* Define to 1 if you have locale.h */
+#define HAVE_LOCALE_H 1
+
+/* Define to 1 if you have poll */
+#define HAVE_POLL 1
+
+/* Define to 1 if you have signal.h */
+#define HAVE_SIGNAL_H 1
+
+/* Define to 1 if you have stdio.h */
+#define HAVE_STDIO_H 1
+
+#define HAVE_STDATOMIC_H 1
+#define HAVE_SYSLOG_H 1
+/* #undef HAVE_SYS_EVENTS_H */
+/* #undef HAVE_SYS_INOTIFY_H */
+/* #undef HAVE_LINUX_MAGIC_H */
+/* #undef HAVE_SYS_PRCTL_H */
+#define HAVE_SYS_RANDOM_H 1
+#define HAVE_SYS_RESOURCE_H 1
+#define HAVE_SYS_SYSCALL_H 1
+/* #undef HAVE_SYS_VFS_H */
+
+/* Define to 1 if you have sys/time.h */
+#define HAVE_SYS_TIME_H 1
+
+/* Define to 1 if you have unistd.h */
+#define HAVE_UNISTD_H 1
+
+/* Define to 1 if you have ws2tcpip.h */
+/* #undef HAVE_WS2TCPIP_H */
+
+// symbols
+/* Define to 1 if you have backtrace */
+#define HAVE_BACKTRACE 1
+
+/* Define to 1 if you have getgrouplist */
+/* #undef HAVE_GETGROUPLIST */
+
+/* Define to 1 if you have getpeerucred */
+/* #undef HAVE_GETPEERUCRED */
+
+/* Define to 1 if you have nanosleep */
+#define HAVE_NANOSLEEP 1
+
+/* Define to 1 if you have getpwnam_r */
+#define HAVE_GETPWNAM_R 1
+
+/* Define to 1 if you have socketpair */
+#define HAVE_SOCKETPAIR 1
+
+/* Define to 1 if you have setenv */
+#define HAVE_SETENV 1
+
+/* Define to 1 if you have unsetenv */
+#define HAVE_UNSETENV 1
+
+/* Define to 1 if you have clearenv */
+/* #undef HAVE_CLEARENV */
+
+/* Define to 1 if you have closefrom */
+/* #undef HAVE_CLOSEFROM */
+
+/* Define to 1 if you have close_range */
+/* #undef HAVE_CLOSE_RANGE */
+
+/* Define to 1 if you have writev */
+#define HAVE_WRITEV 1
+
+/* Define to 1 if you have socklen_t */
+#define HAVE_SOCKLEN_T 1
+
+/* Define to 1 if you have setlocale */
+#define HAVE_SETLOCALE 1
+
+/* Define to 1 if you have localeconv */
+#define HAVE_LOCALECONV 1
+
+/* Define to 1 if you have pip2 */
+/* #undef HAVE_PIPE2 */
+
+/* #undef HAVE_ACCEPT4 */
+
+/* #undef HAVE_FSTATFS */
+/* #undef HAVE_INOTIFY_INIT1 */
+/* #undef HAVE_GETRANDOM */
+#define HAVE_GETRLIMIT 1
+/* #undef HAVE_PRCTL */
+/* #undef HAVE_PRLIMIT */
+#define HAVE_RAISE 1
+#define HAVE_SETRLIMIT 1
+#define HAVE_UNIX_FD_PASSING 1
+/* #undef HAVE_SYSTEMD */
+
+/* Define to use epoll(4) on Linux */
+/* #undef DBUS_HAVE_LINUX_EPOLL */
+
+/* Use the gcc __sync extension */
+#define DBUS_USE_SYNC 1
+
+/* #undef HAVE_SETRESUID */
+/* #undef HAVE_GETRESUID */
+/* whether -export-dynamic was passed to libtool */
+#define DBUS_BUILT_R_DYNAMIC 1
+
+/* Enable GNU extensions on systems that have them. */
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE 1
+#endif
+
+// structs
+/* Define to 1 if you have struct cmsgred */
+/* #undef HAVE_CMSGCRED */
+
+/* #undef FD_SETSIZE */
+
+#define DBUS_USER "messagebus"
+#define DBUS_TEST_USER "nobody"
+/* #undef DBUS_TEST_EXEC */
+/* Where to put test sockets */
+#define DBUS_TEST_SOCKET_DIR ""
+
+// system type defines
+#if defined(_WIN32) || defined(_WIN64) || defined (_WIN32_WCE)
+# define DBUS_WIN
+# ifdef _WIN32_WCE
+# define DBUS_WINCE
+# else
+# define DBUS_WIN32
+ /* Define to the minimum supported Windows version (0x0600 is Vista) */
+# define _WIN32_WINNT 0x0600
+# endif
+#else
+# define DBUS_UNIX
+#endif
+
+#if defined(_WIN32) || defined(_WIN64)
+// mingw mode_t
+# ifdef HAVE_STDIO_H
+# include <stdio.h>
+# endif
+# ifndef _MSC_VER
+# define uid_t int
+# define gid_t int
+# else
+ typedef int mode_t;
+# endif
+#endif // defined(_WIN32) || defined(_WIN64)
+
+#ifdef interface
+#undef interface
+#endif
+
+#ifndef SIGHUP
+#define SIGHUP 1
+#endif
+
+#ifdef DBUS_WIN
+#define FD_SETSIZE
+#endif
+
+#define HAVE_DECL_ENVIRON 0
+#define HAVE_DECL_LOG_PERROR 1
+#define HAVE_DECL_MSG_NOSIGNAL 1
+#define HAVE_DECL_SYS_PIDFD_OPEN 0
+
+#endif // _DBUS_CONFIG_H
diff --git a/src/3rdparty/libdbus/config-windows.h b/src/3rdparty/libdbus/config-windows.h
new file mode 100644
index 00000000..0bf9b046
--- /dev/null
+++ b/src/3rdparty/libdbus/config-windows.h
@@ -0,0 +1,270 @@
+/* config.h. Generated by cmake from config.h.cmake */
+
+#ifndef _DBUS_CONFIG_H
+#define _DBUS_CONFIG_H
+
+#define VERSION "1.15.9"
+
+/* On Windows, we expect to be using msvcrt.dll-compatible printf
+ * (%I64u instead of %llu) unless otherwise specified. This must be
+ * done near the beginning of config.h, before we have included any
+ * system headers that might check the value of this macro. */
+#ifndef __USE_MINGW_ANSI_STDIO
+# define __USE_MINGW_ANSI_STDIO 0
+#endif
+
+/* #undef _FILE_OFFSET_BITS */
+/* #undef _TIME_BITS */
+/* #undef WORDS_BIGENDIAN */
+
+/* Opt-in to modern APIs and thread-safety for Solaris. In the Autotools
+ * build system we do the equivalent of this by appending to CFLAGS
+ * in configure.ac */
+#ifdef __sun
+# define __EXTENSIONS__
+# define _POSIX_PTHREAD_SEMANTICS
+# define _REENTRANT
+# define _XOPEN_SOURCE 500
+#endif
+
+#define DBUS_DATADIR "C:/Program Files (x86)/dbus/share"
+#define DBUS_BINDIR "C:/Program Files (x86)/dbus/bin"
+#define DBUS_PREFIX "C:/Program Files (x86)/dbus"
+#define DBUS_SYSTEM_CONFIG_FILE "share/dbus-1/system.conf"
+#define DBUS_SESSION_CONFIG_FILE "share/dbus-1/session.conf"
+/* #undef DBUS_SESSION_SOCKET_DIR */
+#define DBUS_DAEMON_NAME "dbus-daemon"
+#define DBUS_SYSTEM_BUS_DEFAULT_ADDRESS "unix:path=C:/Program Files (x86)/dbus/var/run/dbus/system_bus_socket"
+#define DBUS_SESSION_BUS_CONNECT_ADDRESS "autolaunch:"
+#define DBUS_MACHINE_UUID_FILE "C:/Program Files (x86)/dbus/var/lib/dbus/machine-id"
+#define DBUS_DAEMONDIR "C:/Program Files (x86)/dbus/bin"
+#define DBUS_RUNSTATEDIR "C:/Program Files (x86)/dbus/var/run"
+
+/* #undef DBUS_ENABLE_STATS */
+#define ENABLE_TRADITIONAL_ACTIVATION
+
+#define TEST_LISTEN ""
+
+// test binaries
+#define DBUS_EXEEXT ".exe"
+
+/* Some dbus features */
+/* #undef DBUS_ENABLE_ANSI */
+#define DBUS_ENABLE_VERBOSE_MODE 1
+/* #undef DBUS_DISABLE_ASSERT */
+#ifndef DBUS_DISABLE_ASSERT
+# define DBUS_ENABLE_ASSERT 1
+#endif
+/* #undef DBUS_DISABLE_CHECKS */
+#ifndef DBUS_DISABLE_CHECKS
+# define DBUS_ENABLE_CHECKS 1
+#endif
+/* #undef DBUS_ENABLE_EMBEDDED_TESTS */
+/* #undef DBUS_ENABLE_MODULAR_TESTS */
+/* #undef DBUS_USE_OUTPUT_DEBUG_STRING */
+
+/* xmldocs */
+/* doxygen */
+/* #undef DBUS_GCOV_ENABLED */
+
+/* selinux */
+/* kqueue */
+/* #undef HAVE_CONSOLE_OWNER_FILE */
+#define DBUS_CONSOLE_OWNER_FILE ""
+
+/* #undef DBUS_BUILD_X11 */
+/* For the moment, the cmake build system doesn't have an equivalent of
+ * the autoconf build system's --disable-x11-autolaunch */
+#ifdef DBUS_BUILD_X11
+# define DBUS_ENABLE_X11_AUTOLAUNCH 1
+#endif
+
+/* #undef DBUS_WITH_GLIB */
+/* #undef GLIB_VERSION_MIN_REQUIRED */
+/* #undef GLIB_VERSION_MAX_ALLOWED */
+
+// headers
+#define HAVE_AFUNIX_H 1
+/* #undef HAVE_ALLOCA_H */
+/* #undef HAVE_BYTESWAP_H */
+/* #undef HAVE_CRT_EXTERNS_H */
+
+/* Define to 1 if you have dirent.h */
+/* #undef HAVE_DIRENT_H */
+
+/* Define to 1 if you have errno.h */
+#define HAVE_ERRNO_H 1
+
+/* Define to 1 if you have inttypes.h */
+#define HAVE_INTTYPES_H 1
+
+/* Define to 1 if you have io.h */
+#define HAVE_IO_H 1
+
+/* Define to 1 if you have locale.h */
+#define HAVE_LOCALE_H 1
+
+/* Define to 1 if you have poll */
+/* #undef HAVE_POLL */
+
+/* Define to 1 if you have signal.h */
+#define HAVE_SIGNAL_H 1
+
+/* Define to 1 if you have stdio.h */
+#define HAVE_STDIO_H 1
+
+/* #undef HAVE_STDATOMIC_H */
+/* #undef HAVE_SYSLOG_H */
+/* #undef HAVE_SYS_EVENTS_H */
+/* #undef HAVE_SYS_INOTIFY_H */
+/* #undef HAVE_LINUX_MAGIC_H */
+/* #undef HAVE_SYS_PRCTL_H */
+/* #undef HAVE_SYS_RANDOM_H */
+/* #undef HAVE_SYS_RESOURCE_H */
+/* #undef HAVE_SYS_SYSCALL_H */
+/* #undef HAVE_SYS_VFS_H */
+
+/* Define to 1 if you have sys/time.h */
+/* #undef HAVE_SYS_TIME_H */
+
+/* Define to 1 if you have unistd.h */
+/* #undef HAVE_UNISTD_H */
+
+/* Define to 1 if you have ws2tcpip.h */
+#define HAVE_WS2TCPIP_H 1
+
+// symbols
+/* Define to 1 if you have backtrace */
+/* #undef HAVE_BACKTRACE */
+
+/* Define to 1 if you have getgrouplist */
+/* #undef HAVE_GETGROUPLIST */
+
+/* Define to 1 if you have getpeerucred */
+/* #undef HAVE_GETPEERUCRED */
+
+/* Define to 1 if you have nanosleep */
+/* #undef HAVE_NANOSLEEP */
+
+/* Define to 1 if you have getpwnam_r */
+/* #undef HAVE_GETPWNAM_R */
+
+/* Define to 1 if you have socketpair */
+/* #undef HAVE_SOCKETPAIR */
+
+/* Define to 1 if you have setenv */
+/* #undef HAVE_SETENV */
+
+/* Define to 1 if you have unsetenv */
+/* #undef HAVE_UNSETENV */
+
+/* Define to 1 if you have clearenv */
+/* #undef HAVE_CLEARENV */
+
+/* Define to 1 if you have closefrom */
+/* #undef HAVE_CLOSEFROM */
+
+/* Define to 1 if you have close_range */
+/* #undef HAVE_CLOSE_RANGE */
+
+/* Define to 1 if you have writev */
+/* #undef HAVE_WRITEV */
+
+/* Define to 1 if you have socklen_t */
+/* #undef HAVE_SOCKLEN_T */
+
+/* Define to 1 if you have setlocale */
+#define HAVE_SETLOCALE 1
+
+/* Define to 1 if you have localeconv */
+#define HAVE_LOCALECONV 1
+
+/* Define to 1 if you have pip2 */
+/* #undef HAVE_PIPE2 */
+
+/* #undef HAVE_ACCEPT4 */
+
+/* #undef HAVE_FSTATFS */
+/* #undef HAVE_INOTIFY_INIT1 */
+/* #undef HAVE_GETRANDOM */
+/* #undef HAVE_GETRLIMIT */
+/* #undef HAVE_PRCTL */
+/* #undef HAVE_PRLIMIT */
+#define HAVE_RAISE 1
+/* #undef HAVE_SETRLIMIT */
+/* #undef HAVE_UNIX_FD_PASSING */
+/* #undef HAVE_SYSTEMD */
+
+/* Define to use epoll(4) on Linux */
+/* #undef DBUS_HAVE_LINUX_EPOLL */
+
+/* Use the gcc __sync extension */
+#define DBUS_USE_SYNC 0
+
+/* #undef HAVE_SETRESUID */
+/* #undef HAVE_GETRESUID */
+/* whether -export-dynamic was passed to libtool */
+/* #undef DBUS_BUILT_R_DYNAMIC */
+
+/* Enable GNU extensions on systems that have them. */
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE 1
+#endif
+
+// structs
+/* Define to 1 if you have struct cmsgred */
+/* #undef HAVE_CMSGCRED */
+
+#define FD_SETSIZE 8192
+
+#define DBUS_USER "Administrator"
+#define DBUS_TEST_USER "guest"
+/* #undef DBUS_TEST_EXEC */
+/* Where to put test sockets */
+#define DBUS_TEST_SOCKET_DIR ""
+
+// system type defines
+#if defined(_WIN32) || defined(_WIN64) || defined (_WIN32_WCE)
+# define DBUS_WIN
+# ifdef _WIN32_WCE
+# define DBUS_WINCE
+# else
+# define DBUS_WIN32
+ /* Define to the minimum supported Windows version (0x0600 is Vista) */
+# define _WIN32_WINNT 0x0600
+# endif
+#else
+# define DBUS_UNIX
+#endif
+
+#if defined(_WIN32) || defined(_WIN64)
+// mingw mode_t
+# ifdef HAVE_STDIO_H
+# include <stdio.h>
+# endif
+# ifndef _MSC_VER
+# define uid_t int
+# define gid_t int
+# else
+ typedef int mode_t;
+# endif
+#endif // defined(_WIN32) || defined(_WIN64)
+
+#ifdef interface
+#undef interface
+#endif
+
+#ifndef SIGHUP
+#define SIGHUP 1
+#endif
+
+#ifdef DBUS_WIN
+#define FD_SETSIZE 8192
+#endif
+
+#define HAVE_DECL_ENVIRON 0
+#define HAVE_DECL_LOG_PERROR 0
+#define HAVE_DECL_MSG_NOSIGNAL 0
+#define HAVE_DECL_SYS_PIDFD_OPEN 0
+
+#endif // _DBUS_CONFIG_H
diff --git a/src/3rdparty/libdbus/config.h b/src/3rdparty/libdbus/config.h
new file mode 100644
index 00000000..056cc2d3
--- /dev/null
+++ b/src/3rdparty/libdbus/config.h
@@ -0,0 +1,5 @@
+#if defined(PLATFORM_CONFIG_H)
+# include PLATFORM_CONFIG_H
+#else
+# error "This libdbus build is not available for this platform"
+#endif
diff --git a/src/3rdparty/libdbus/dbus-arch-deps-macos.h b/src/3rdparty/libdbus/dbus-arch-deps-macos.h
new file mode 100644
index 00000000..0b7d0611
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus-arch-deps-macos.h
@@ -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 long dbus_int64_t;
+_DBUS_GNUC_EXTENSION typedef unsigned long dbus_uint64_t;
+#define DBUS_INT64_MODIFIER "l"
+
+#define DBUS_INT64_CONSTANT(val) (_DBUS_GNUC_EXTENSION (val##L))
+#define DBUS_UINT64_CONSTANT(val) (_DBUS_GNUC_EXTENSION (val##UL))
+
+typedef int dbus_int32_t;
+typedef unsigned int dbus_uint32_t;
+
+typedef short dbus_int16_t;
+typedef unsigned short dbus_uint16_t;
+
+#define DBUS_SIZEOF_VOID_P 8
+
+/* This is not really arch-dependent, but it's not worth
+ * creating an additional generated header just for this
+ */
+#define DBUS_MAJOR_VERSION 1
+#define DBUS_MINOR_VERSION 15
+#define DBUS_MICRO_VERSION 9
+
+#define DBUS_VERSION_STRING "1.15.9"
+
+#define DBUS_VERSION ((1 << 16) | (15 << 8) | (9))
+
+DBUS_END_DECLS
+
+#endif /* DBUS_ARCH_DEPS_H */
diff --git a/src/3rdparty/libdbus/dbus-arch-deps-windows.h b/src/3rdparty/libdbus/dbus-arch-deps-windows.h
new file mode 100644
index 00000000..7e95bb90
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus-arch-deps-windows.h
@@ -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 long long dbus_int64_t;
+_DBUS_GNUC_EXTENSION typedef unsigned long long dbus_uint64_t;
+#define DBUS_INT64_MODIFIER "I64"
+
+#define DBUS_INT64_CONSTANT(val) (_DBUS_GNUC_EXTENSION (val##LL))
+#define DBUS_UINT64_CONSTANT(val) (_DBUS_GNUC_EXTENSION (val##ULL))
+
+typedef int dbus_int32_t;
+typedef unsigned int dbus_uint32_t;
+
+typedef short dbus_int16_t;
+typedef unsigned short dbus_uint16_t;
+
+#define DBUS_SIZEOF_VOID_P 8
+
+/* This is not really arch-dependent, but it's not worth
+ * creating an additional generated header just for this
+ */
+#define DBUS_MAJOR_VERSION 1
+#define DBUS_MINOR_VERSION 15
+#define DBUS_MICRO_VERSION 9
+
+#define DBUS_VERSION_STRING "1.15.9"
+
+#define DBUS_VERSION ((1 << 16) | (15 << 8) | (9))
+
+DBUS_END_DECLS
+
+#endif /* DBUS_ARCH_DEPS_H */
diff --git a/src/3rdparty/libdbus/dbus-server-launchd.c b/src/3rdparty/libdbus/dbus-server-launchd.c
new file mode 100644
index 00000000..b28b4576
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus-server-launchd.c
@@ -0,0 +1,214 @@
+/* dbus-server-launchd.c Server methods for interacting with launchd.
+ * Copyright (C) 2007, Tanner Lovelace <lovelace@wayfarer.org>
+ * Copyright (C) 2008, Colin Walters <walters@verbum.org>
+ * Copyright (C) 2008-2009, Benjamin Reed <rangerrick@befunk.com>
+ * Copyright (C) 2009, Jonas Bähr <jonas.baehr@web.de>
+ * 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.
+ */
+
+#include <config.h>
+#include "dbus-server-launchd.h"
+
+/**
+ * @defgroup DBusServerLaunchd DBusServer implementations for Launchd
+ * @ingroup DBusInternals
+ * @brief Implementation details of DBusServer with Launchd support
+ *
+ * @{
+ */
+
+#ifdef DBUS_ENABLE_LAUNCHD
+#include <launch.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "dbus-misc.h"
+#include "dbus-server-socket.h"
+#include "dbus-sysdeps-unix.h"
+
+/* put other private launchd functions here */
+
+#endif /* DBUS_ENABLE_LAUNCHD */
+
+/**
+ * @brief Creates a new server from launchd.
+ *
+ * launchd has allocaed a socket for us. We now query launchd for the
+ * file descriptor of this socket and create a server on it.
+ * In addition we inherit launchd's environment which holds a variable
+ * containing the path to the socket. This is used to init the server's
+ * address which is passed to autolaunched services.
+ *
+ * @param launchd_env_var the environment variable which holds the unix path to the socket
+ * @param error location to store reason for failure.
+ * @returns the new server, or #NULL on failure.
+ */
+
+DBusServer *
+_dbus_server_new_for_launchd (const char *launchd_env_var, DBusError * error)
+ {
+#ifdef DBUS_ENABLE_LAUNCHD
+ DBusServer *server;
+ DBusString address;
+ int launchd_fd = -1;
+ launch_data_t sockets_dict, checkin_response;
+ launch_data_t checkin_request;
+ launch_data_t listening_fd_array, listening_fd;
+ launch_data_t environment_dict, environment_param;
+ const char *launchd_socket_path, *display;
+
+ launchd_socket_path = _dbus_getenv (launchd_env_var);
+ display = _dbus_getenv ("DISPLAY");
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ if (launchd_socket_path == NULL || *launchd_socket_path == '\0')
+ {
+ dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS,
+ "launchd's environment variable %s is empty, but should contain a socket path.\n", launchd_env_var);
+ return NULL;
+ }
+
+ if (!_dbus_string_init (&address))
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ return NULL;
+ }
+ if (!_dbus_string_append (&address, "unix:path="))
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ goto l_failed_0;
+ }
+ if (!_dbus_string_append (&address, launchd_socket_path))
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ goto l_failed_0;
+ }
+
+ if ((checkin_request = launch_data_new_string (LAUNCH_KEY_CHECKIN)) == NULL)
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY,
+ "launch_data_new_string(\"%s\") Unable to create string.\n",
+ LAUNCH_KEY_CHECKIN);
+ goto l_failed_0;
+ }
+
+ if ((checkin_response = launch_msg (checkin_request)) == NULL)
+ {
+ dbus_set_error (error, DBUS_ERROR_IO_ERROR,
+ "launch_msg(\"%s\") IPC failure: %s\n",
+ LAUNCH_KEY_CHECKIN, strerror (errno));
+ goto l_failed_0;
+ }
+
+ if (LAUNCH_DATA_ERRNO == launch_data_get_type (checkin_response))
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED, "Check-in failed: %s\n",
+ strerror (launch_data_get_errno (checkin_response)));
+ goto l_failed_0;
+ }
+
+ sockets_dict =
+ launch_data_dict_lookup (checkin_response, LAUNCH_JOBKEY_SOCKETS);
+ if (NULL == sockets_dict)
+ {
+ dbus_set_error (error, DBUS_ERROR_IO_ERROR,
+ "No sockets found to answer requests on!\n");
+ goto l_failed_0;
+ }
+
+ listening_fd_array =
+ launch_data_dict_lookup (sockets_dict, "unix_domain_listener");
+ if (NULL == listening_fd_array)
+ {
+ dbus_set_error (error, DBUS_ERROR_IO_ERROR,
+ "No known sockets found to answer requests on!\n");
+ goto l_failed_0;
+ }
+
+ if (launch_data_array_get_count (listening_fd_array) != 1)
+ {
+ dbus_set_error (error, DBUS_ERROR_LIMITS_EXCEEDED,
+ "Expected 1 socket from launchd, got %d.\n",
+ launch_data_array_get_count (listening_fd_array));
+ goto l_failed_0;
+ }
+
+ listening_fd = launch_data_array_get_index (listening_fd_array, 0);
+ launchd_fd = launch_data_get_fd (listening_fd);
+
+ _dbus_fd_set_close_on_exec (launchd_fd);
+
+ if (launchd_fd < 0)
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ goto l_failed_0;
+ if (display == NULL || *display == '\0')
+ {
+ environment_dict = launch_data_dict_lookup (checkin_response, LAUNCH_JOBKEY_USERENVIRONMENTVARIABLES);
+ if (NULL == environment_dict)
+ {
+ _dbus_warn ("Unable to retrieve user environment from launchd.");
+ }
+ else
+ {
+ environment_param = launch_data_dict_lookup (environment_dict, "DISPLAY");
+ if (NULL == environment_param)
+ {
+ _dbus_warn ("Unable to retrieve DISPLAY from launchd.");
+ }
+ else
+ {
+ display = launch_data_get_string(environment_param);
+ dbus_setenv ("DISPLAY", display);
+ }
+ }
+ }
+
+ }
+
+ server = _dbus_server_new_for_socket (&launchd_fd, 1, &address, 0, error);
+ if (server == NULL)
+ {
+ goto l_failed_0;
+ }
+
+ _dbus_string_free (&address);
+
+ return server;
+
+ l_failed_0:
+ if (launchd_fd >= 0)
+ close (launchd_fd);
+
+ _dbus_string_free (&address);
+
+ return NULL;
+#else /* DBUS_ENABLE_LAUNCHD */
+ dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS,
+ "address type 'launchd' requested, but launchd support not compiled in");
+ return NULL;
+#endif
+ }
+
+/** @} */
diff --git a/src/3rdparty/libdbus/dbus-server-launchd.h b/src/3rdparty/libdbus/dbus-server-launchd.h
new file mode 100644
index 00000000..51c9628e
--- /dev/null
+++ b/src/3rdparty/libdbus/dbus-server-launchd.h
@@ -0,0 +1,37 @@
+/* dbus-server-launchd.h Server methods for interacting with launchd.
+* Copyright (C) 2008, Benjamin Reed <rangerrick@befunk.com>
+* 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_SERVER_LAUNCHD_H
+#define DBUS_SERVER_LAUNCHD_H
+
+#include <dbus/dbus-internals.h>
+#include <dbus/dbus-server-protected.h>
+
+DBUS_BEGIN_DECLS
+
+DBusServer * _dbus_server_new_for_launchd (const char *launchd_env_var, DBusError * error);
+
+DBUS_END_DECLS
+#endif /* DBUS_SERVER_LAUNCHD_H */
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 */
diff --git a/src/3rdparty/libdbus/qt_attribution.json b/src/3rdparty/libdbus/qt_attribution.json
new file mode 100644
index 00000000..177cd271
--- /dev/null
+++ b/src/3rdparty/libdbus/qt_attribution.json
@@ -0,0 +1,17 @@
+{
+ "Id": "libdbus",
+ "Name": "libdbus",
+ "QDocModule": "qtapplicationmanager",
+ "QtUsage": "Optionally used in Qt ApplicationManager on Windows and macOS. Configure with --libdbus=no to avoid.",
+ "SecurityCritical": false,
+
+ "Description": "libdbus is the reference implementation of D-Bus.",
+ "Homepage": "https://www.freedesktop.org/wiki/Software/dbus/",
+ "Version": "1.15.9",
+ "DownloadLocation": "https://dbus.freedesktop.org/releases/dbus/dbus-1.15.9.tar.gz",
+
+ "License": "Academic Free License v2.1, or GNU General Public License v2.0 or later",
+ "LicenseId": "AFL-2.1 OR GPL-2.0-or-later",
+ "LicenseFile": "COPYING",
+ "Copyright": "Copyright the DBus project authors."
+}
diff --git a/src/common-lib/configure.cmake b/src/common-lib/configure.cmake
index c33f6ba2..11f5d8ef 100644
--- a/src/common-lib/configure.cmake
+++ b/src/common-lib/configure.cmake
@@ -85,6 +85,14 @@ qt_feature("am-dltlogging" PRIVATE
DISABLE INPUT_dltlogging STREQUAL 'no'
)
+qt_feature("am-libdbus" PRIVATE
+ LABEL "Compile libdbus for appman-controller"
+ CONDITION WIN32 OR MACOS
+ EMIT_IF WIN32 OR MACOS
+ ENABLE INPUT_libdbus STREQUAL 'yes'
+ DISABLE INPUT_libdbus STREQUAL 'no'
+)
+
qt_feature("am-libbacktrace" PRIVATE
LABEL "Enable support for libbacktrace"
CONDITION (LINUX OR MACOS) AND (CMAKE_BUILD_TYPE STREQUAL "Debug")
@@ -134,6 +142,7 @@ qt_configure_add_summary_entry(ARGS "am-widgets-support")
qt_configure_add_summary_entry(ARGS "am-tools-only")
qt_configure_add_summary_entry(ARGS "am-package-server")
qt_configure_add_summary_entry(ARGS "am-dltlogging")
+qt_configure_add_summary_entry(ARGS "am-libdbus")
qt_configure_add_summary_entry(ARGS "am-libbacktrace")
qt_configure_add_summary_entry(ARGS "am-stackwalker")
qt_configure_end_summary_section() # end of "Qt ApplicationManger" section
diff --git a/src/common-lib/dbus-utilities.cpp b/src/common-lib/dbus-utilities.cpp
index 0f4522b1..fd944fe8 100644
--- a/src/common-lib/dbus-utilities.cpp
+++ b/src/common-lib/dbus-utilities.cpp
@@ -12,10 +12,22 @@
# include <QDBusArgument>
# include <QDBusMetaType>
# include <QDBusUnixFileDescriptor>
+# include <QLibrary>
+# include <QLibraryInfo>
+# include <QDir>
#endif
-
+#if defined(Q_OS_WIN)
+# include <windows.h>
+# ifdef interface
+# undef interface
+# endif
+#endif
+#include "logging.h"
#include "dbus-utilities.h"
+using namespace Qt::StringLiterals;
+
+
QT_BEGIN_NAMESPACE_AM
QVariant convertFromJSVariant(const QVariant &variant)
@@ -132,6 +144,52 @@ void registerDBusTypes()
#endif
}
+void ensureLibDBusIsAvailable()
+{
+#if (defined(Q_OS_WINDOWS) || defined(Q_OS_MACOS)) && defined(QT_DBUS_LIB)
+ // On Windows and macOS, libdbus-1 is not readily available, but we need it to communicate
+ // between appman and appman-controller.
+ // We first check if the user has a custom libdbus-1 installed already. If not, we load the
+ // one that comes with the application manager.
+# if defined(Q_OS_WINDOWS)
+ static const QString dbusLibName = u"dbus-1"_s;
+ auto dbusLoadPrepare = []() {
+ const QString dllPath = QLibraryInfo::path(QLibraryInfo::BinariesPath)
+ + u"/qtapplicationmanager";
+ ::SetDllDirectoryW((LPCWSTR) dllPath.utf16());
+ };
+ auto dbusLoadCleanup = []() {
+ ::SetDllDirectoryW(nullptr);
+ };
+
+# elif defined(Q_OS_MACOS)
+ static const QString dbusLibName = u"libdbus-1"_s;
+ QString currentPath;
+ auto dbusLoadPrepare = [&currentPath]() {
+ const QString dylibPath = QLibraryInfo::path(QLibraryInfo::LibrariesPath)
+ + u"/qtapplicationmanager";
+ // adding to DYLD_LIBRARY_PATH has no effect on the running process
+ currentPath = QDir::currentPath();
+ QDir::setCurrent(dylibPath);
+ };
+ auto dbusLoadCleanup = [&currentPath]() {
+ QDir::setCurrent(currentPath);
+ };
+# endif
+
+ static QLibrary dbusLib(dbusLibName);
+ if (!dbusLib.isLoaded() && !dbusLib.load()) {
+ dbusLoadPrepare();
+
+ if (!dbusLib.load() || !dbusLib.resolve("dbus_connection_open_private"))
+ qCCritical(LogDBus) << "WARNING: could not load the application manager's libdbus-1 for appman-controller support.";
+ else
+ qCInfo(LogDBus) << "Loaded the application manager's libdbus-1 for appman-controller support.";
+ dbusLoadCleanup();
+ }
+#endif
+}
+
QT_END_NAMESPACE_AM
#if defined(QT_DBUS_LIB)
diff --git a/src/common-lib/dbus-utilities.h b/src/common-lib/dbus-utilities.h
index bb5a9c4c..fc0bce8c 100644
--- a/src/common-lib/dbus-utilities.h
+++ b/src/common-lib/dbus-utilities.h
@@ -27,6 +27,8 @@ QVariant convertFromDBusVariant(const QVariant &variant);
void registerDBusTypes();
+void ensureLibDBusIsAvailable();
+
QT_END_NAMESPACE_AM
#endif // DBUS_UTILITIES_H
diff --git a/src/common-lib/qt_cmdline.cmake b/src/common-lib/qt_cmdline.cmake
index 66dce390..6d20449d 100644
--- a/src/common-lib/qt_cmdline.cmake
+++ b/src/common-lib/qt_cmdline.cmake
@@ -5,6 +5,7 @@ qt_commandline_option(external-dbus-interfaces TYPE boolean)
qt_commandline_option(tools-only TYPE boolean)
qt_commandline_option(package-server TYPE boolean)
qt_commandline_option(dltlogging TYPE boolean)
+qt_commandline_option(libdbus TYPE boolean)
qt_commandline_option(libbacktrace TYPE boolean)
qt_commandline_option(stackwalker TYPE boolean)
qt_commandline_option(libyaml TYPE enum VALUES qt system)
diff --git a/src/shared-main-lib/sharedmain.cpp b/src/shared-main-lib/sharedmain.cpp
index 1b8cd058..7caf9780 100644
--- a/src/shared-main-lib/sharedmain.cpp
+++ b/src/shared-main-lib/sharedmain.cpp
@@ -3,7 +3,6 @@
// Copyright (C) 2018 Pelagicore AG
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
-#include <memory>
#include <qglobal.h>
#include <QFile>
@@ -31,13 +30,12 @@
#include "sharedmain.h"
#include "utilities.h"
+#include "dbus-utilities.h"
#include "exception.h"
#include "crashhandler.h"
#include "startuptimer.h"
#include "unixsignalhandler.h"
-#include "../plugin-interfaces/startupinterface.h"
-
using namespace Qt::StringLiterals;
@@ -101,6 +99,8 @@ void SharedMain::initialize()
if (!qEnvironmentVariableIsSet("XDG_RUNTIME_DIR"))
setenv("XDG_RUNTIME_DIR", QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation).toLocal8Bit(), 1);
#endif
+
+ ensureLibDBusIsAvailable();
}
// We need to do some things BEFORE the Q*Application constructor runs, so we're using this
diff --git a/src/tools/controller/controller.cpp b/src/tools/controller/controller.cpp
index 48b0abe5..2fb3ed3b 100644
--- a/src/tools/controller/controller.cpp
+++ b/src/tools/controller/controller.cpp
@@ -345,6 +345,8 @@ int main(int argc, char *argv[])
QCoreApplication::setOrganizationDomain(u"qt-project.org"_s);
QCoreApplication::setApplicationVersion(QString::fromLatin1(QT_AM_VERSION_STR));
+ ensureLibDBusIsAvailable(); // this needs to happen before the QCoreApplication constructor
+
ThrowingApplication a(argc, argv);
QByteArray desc = "\n\nAvailable commands are:\n";