1 /* GDBus - GLib D-Bus Library
3 * Copyright (C) 2008-2009 Red Hat, Inc.
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General
16 * Public License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
18 * Boston, MA 02111-1307, USA.
20 * Author: David Zeuthen <davidz@redhat.com>
28 #include "gdbusutils.h"
29 #include "gdbusaddress.h"
30 #include "gdbuserror.h"
31 #include "gioenumtypes.h"
32 #include "gdbusprivate.h"
35 #include <gio/gunixsocketaddress.h>
42 * SECTION:gdbusaddress
43 * @title: D-Bus Addresses
44 * @short_description: D-Bus connection endpoints
47 * Routines for working with D-Bus addresses. A D-Bus address is a string
48 * like "unix:tmpdir=/tmp/my-app-name". The exact format of addresses
49 * is explained in detail in the <link linkend="http://dbus.freedesktop.org/doc/dbus-specification.html#addresses">D-Bus specification</link>.
52 /* ---------------------------------------------------------------------------------------------------- */
58 * Checks if @string is a D-Bus address.
60 * This doesn't check if @string is actually supported by #GDBusServer
61 * or #GDBusConnection - use g_dbus_is_supported_address() to do more
64 * Returns: %TRUE if @string is a valid D-Bus address, %FALSE otherwise.
69 g_dbus_is_address (const gchar *string)
77 g_return_val_if_fail (string != NULL, FALSE);
79 a = g_strsplit (string, ";", 0);
80 for (n = 0; a[n] != NULL; n++)
82 if (!_g_dbus_address_parse_entry (a[n],
97 is_valid_unix (const gchar *address_entry,
98 GHashTable *key_value_pairs,
106 const gchar *abstract;
114 keys = g_hash_table_get_keys (key_value_pairs);
115 for (l = keys; l != NULL; l = l->next)
117 const gchar *key = l->data;
118 if (g_strcmp0 (key, "path") == 0)
119 path = g_hash_table_lookup (key_value_pairs, key);
120 else if (g_strcmp0 (key, "tmpdir") == 0)
121 tmpdir = g_hash_table_lookup (key_value_pairs, key);
122 else if (g_strcmp0 (key, "abstract") == 0)
123 abstract = g_hash_table_lookup (key_value_pairs, key);
128 G_IO_ERROR_INVALID_ARGUMENT,
129 _("Unsupported key `%s' in address entry `%s'"),
138 if (tmpdir != NULL || abstract != NULL)
140 /* TODO: validate path */
142 else if (tmpdir != NULL)
144 if (path != NULL || abstract != NULL)
146 /* TODO: validate tmpdir */
148 else if (abstract != NULL)
150 if (path != NULL || tmpdir != NULL)
152 /* TODO: validate abstract */
158 G_IO_ERROR_INVALID_ARGUMENT,
159 _("Address `%s' is invalid (need exactly one of path, tmpdir or abstract keys"),
171 G_IO_ERROR_INVALID_ARGUMENT,
172 _("Meaningless key/value pair combination in address entry `%s'"),
182 is_valid_nonce_tcp (const gchar *address_entry,
183 GHashTable *key_value_pairs,
192 const gchar *nonce_file;
203 keys = g_hash_table_get_keys (key_value_pairs);
204 for (l = keys; l != NULL; l = l->next)
206 const gchar *key = l->data;
207 if (g_strcmp0 (key, "host") == 0)
208 host = g_hash_table_lookup (key_value_pairs, key);
209 else if (g_strcmp0 (key, "port") == 0)
210 port = g_hash_table_lookup (key_value_pairs, key);
211 else if (g_strcmp0 (key, "family") == 0)
212 family = g_hash_table_lookup (key_value_pairs, key);
213 else if (g_strcmp0 (key, "noncefile") == 0)
214 nonce_file = g_hash_table_lookup (key_value_pairs, key);
219 G_IO_ERROR_INVALID_ARGUMENT,
220 _("Unsupported key `%s' in address entry `%s'"),
229 port_num = strtol (port, &endp, 10);
230 if ((*port == '\0' || *endp != '\0') || port_num < 0 || port_num >= 65536)
234 G_IO_ERROR_INVALID_ARGUMENT,
235 _("Error in address `%s' - the port attribute is malformed"),
241 if (family != NULL && !(g_strcmp0 (family, "ipv4") == 0 || g_strcmp0 (family, "ipv6") == 0))
245 G_IO_ERROR_INVALID_ARGUMENT,
246 _("Error in address `%s' - the family attribute is malformed"),
260 is_valid_tcp (const gchar *address_entry,
261 GHashTable *key_value_pairs,
279 keys = g_hash_table_get_keys (key_value_pairs);
280 for (l = keys; l != NULL; l = l->next)
282 const gchar *key = l->data;
283 if (g_strcmp0 (key, "host") == 0)
284 host = g_hash_table_lookup (key_value_pairs, key);
285 else if (g_strcmp0 (key, "port") == 0)
286 port = g_hash_table_lookup (key_value_pairs, key);
287 else if (g_strcmp0 (key, "family") == 0)
288 family = g_hash_table_lookup (key_value_pairs, key);
293 G_IO_ERROR_INVALID_ARGUMENT,
294 _("Unsupported key `%s' in address entry `%s'"),
303 port_num = strtol (port, &endp, 10);
304 if ((*port == '\0' || *endp != '\0') || port_num < 0 || port_num >= 65536)
308 G_IO_ERROR_INVALID_ARGUMENT,
309 _("Error in address `%s' - the port attribute is malformed"),
315 if (family != NULL && !(g_strcmp0 (family, "ipv4") == 0 || g_strcmp0 (family, "ipv6") == 0))
319 G_IO_ERROR_INVALID_ARGUMENT,
320 _("Error in address `%s' - the family attribute is malformed"),
334 * g_dbus_is_supported_address:
336 * @error: Return location for error or %NULL.
338 * Like g_dbus_is_address() but also checks if the library suppors the
339 * transports in @string and that key/value pairs for each transport
342 * Returns: %TRUE if @string is a valid D-Bus address that is
343 * supported by this library, %FALSE if @error is set.
348 g_dbus_is_supported_address (const gchar *string,
357 g_return_val_if_fail (string != NULL, FALSE);
358 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
360 a = g_strsplit (string, ";", 0);
361 for (n = 0; a[n] != NULL; n++)
363 gchar *transport_name;
364 GHashTable *key_value_pairs;
367 if (!_g_dbus_address_parse_entry (a[n],
374 if (g_strcmp0 (transport_name, "unix") == 0)
375 supported = is_valid_unix (a[n], key_value_pairs, error);
376 else if (g_strcmp0 (transport_name, "tcp") == 0)
377 supported = is_valid_tcp (a[n], key_value_pairs, error);
378 else if (g_strcmp0 (transport_name, "nonce-tcp") == 0)
379 supported = is_valid_nonce_tcp (a[n], key_value_pairs, error);
381 g_free (transport_name);
382 g_hash_table_unref (key_value_pairs);
393 g_assert (ret || (!ret && (error == NULL || *error != NULL)));
399 _g_dbus_address_parse_entry (const gchar *address_entry,
400 gchar **out_transport_name,
401 GHashTable **out_key_value_pairs,
405 GHashTable *key_value_pairs;
406 gchar *transport_name;
413 transport_name = NULL;
414 key_value_pairs = NULL;
416 s = strchr (address_entry, ':');
421 G_IO_ERROR_INVALID_ARGUMENT,
422 _("Address element `%s', does not contain a colon (:)"),
427 transport_name = g_strndup (address_entry, s - address_entry);
428 key_value_pairs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
430 kv_pairs = g_strsplit (s + 1, ",", 0);
431 for (n = 0; kv_pairs != NULL && kv_pairs[n] != NULL; n++)
433 const gchar *kv_pair = kv_pairs[n];
437 s = strchr (kv_pair, '=');
442 G_IO_ERROR_INVALID_ARGUMENT,
443 _("Key/Value pair %d, `%s', in address element `%s', does not contain an equal sign"),
450 /* TODO: actually validate that no illegal characters are present before and after then '=' sign */
451 key = g_uri_unescape_segment (kv_pair, s, NULL);
452 value = g_uri_unescape_segment (s + 1, kv_pair + strlen (kv_pair), NULL);
453 g_hash_table_insert (key_value_pairs, key, value);
459 g_strfreev (kv_pairs);
462 if (out_transport_name != NULL)
463 *out_transport_name = transport_name;
465 g_free (transport_name);
466 if (out_key_value_pairs != NULL)
467 *out_key_value_pairs = key_value_pairs;
468 else if (key_value_pairs != NULL)
469 g_hash_table_unref (key_value_pairs);
473 g_free (transport_name);
474 if (key_value_pairs != NULL)
475 g_hash_table_unref (key_value_pairs);
480 /* ---------------------------------------------------------------------------------------------------- */
482 /* TODO: Declare an extension point called GDBusTransport (or similar)
483 * and move code below to extensions implementing said extension
484 * point. That way we can implement a D-Bus transport over X11 without
485 * making libgio link to libX11...
488 g_dbus_address_connect (const gchar *address_entry,
489 const gchar *transport_name,
490 GHashTable *key_value_pairs,
491 GCancellable *cancellable,
495 GSocketConnectable *connectable;
496 const gchar *nonce_file;
506 else if (g_strcmp0 (transport_name, "unix") == 0)
509 const gchar *abstract;
510 path = g_hash_table_lookup (key_value_pairs, "path");
511 abstract = g_hash_table_lookup (key_value_pairs, "abstract");
512 if ((path == NULL && abstract == NULL) || (path != NULL && abstract != NULL))
516 G_IO_ERROR_INVALID_ARGUMENT,
517 _("Error in address `%s' - the unix transport requires exactly one of the "
518 "keys `path' or `abstract' to be set"),
521 else if (path != NULL)
523 connectable = G_SOCKET_CONNECTABLE (g_unix_socket_address_new (path));
525 else if (abstract != NULL)
527 connectable = G_SOCKET_CONNECTABLE (g_unix_socket_address_new_with_type (abstract,
529 G_UNIX_SOCKET_ADDRESS_ABSTRACT));
533 g_assert_not_reached ();
537 else if (g_strcmp0 (transport_name, "tcp") == 0 || g_strcmp0 (transport_name, "nonce-tcp") == 0)
545 is_nonce = (g_strcmp0 (transport_name, "nonce-tcp") == 0);
547 host = g_hash_table_lookup (key_value_pairs, "host");
552 G_IO_ERROR_INVALID_ARGUMENT,
553 _("Error in address `%s' - the host attribute is missing or malformed"),
558 s = g_hash_table_lookup (key_value_pairs, "port");
561 port = strtol (s, &endp, 10);
562 if ((*s == '\0' || *endp != '\0') || port < 0 || port >= 65536)
566 G_IO_ERROR_INVALID_ARGUMENT,
567 _("Error in address `%s' - the port attribute is missing or malformed"),
575 nonce_file = g_hash_table_lookup (key_value_pairs, "noncefile");
576 if (nonce_file == NULL)
580 G_IO_ERROR_INVALID_ARGUMENT,
581 _("Error in address `%s' - the noncefile attribute is missing or malformed"),
587 /* TODO: deal with family */
588 connectable = g_network_address_new (host, port);
594 G_IO_ERROR_INVALID_ARGUMENT,
595 _("Unknown or unsupported transport `%s' for address `%s'"),
600 if (connectable != NULL)
602 GSocketClient *client;
603 GSocketConnection *connection;
605 g_assert (ret == NULL);
606 client = g_socket_client_new ();
607 connection = g_socket_client_connect (client,
611 g_object_unref (connectable);
612 g_object_unref (client);
613 if (connection == NULL)
616 ret = G_IO_STREAM (connection);
618 if (nonce_file != NULL)
620 gchar *nonce_contents;
623 /* TODO: too dangerous to read the entire file? (think denial-of-service etc.) */
624 if (!g_file_get_contents (nonce_file,
629 g_prefix_error (error, _("Error reading nonce file `%s':"), nonce_file);
630 g_object_unref (ret);
635 if (nonce_length != 16)
639 G_IO_ERROR_INVALID_ARGUMENT,
640 _("The nonce-file `%s' was %" G_GSIZE_FORMAT " bytes. Expected 16 bytes."),
643 g_free (nonce_contents);
644 g_object_unref (ret);
649 if (!g_output_stream_write_all (g_io_stream_get_output_stream (ret),
656 g_prefix_error (error, _("Error write contents of nonce file `%s' to stream:"), nonce_file);
657 g_object_unref (ret);
659 g_free (nonce_contents);
662 g_free (nonce_contents);
672 g_dbus_address_try_connect_one (const gchar *address_entry,
674 GCancellable *cancellable,
678 GHashTable *key_value_pairs;
679 gchar *transport_name;
683 transport_name = NULL;
684 key_value_pairs = NULL;
686 if (!_g_dbus_address_parse_entry (address_entry,
692 ret = g_dbus_address_connect (address_entry,
700 /* TODO: validate that guid is of correct format */
701 guid = g_hash_table_lookup (key_value_pairs, "guid");
702 if (guid != NULL && out_guid != NULL)
703 *out_guid = g_strdup (guid);
706 g_free (transport_name);
707 if (key_value_pairs != NULL)
708 g_hash_table_unref (key_value_pairs);
713 /* ---------------------------------------------------------------------------------------------------- */
722 get_stream_data_free (GetStreamData *data)
724 g_free (data->address);
725 if (data->stream != NULL)
726 g_object_unref (data->stream);
732 get_stream_thread_func (GSimpleAsyncResult *res,
734 GCancellable *cancellable)
739 data = g_simple_async_result_get_op_res_gpointer (res);
742 data->stream = g_dbus_address_get_stream_sync (data->address,
746 if (data->stream == NULL)
748 g_simple_async_result_set_from_error (res, error);
749 g_error_free (error);
754 * g_dbus_address_get_stream:
755 * @address: A valid D-Bus address.
756 * @cancellable: A #GCancellable or %NULL.
757 * @callback: A #GAsyncReadyCallback to call when the request is satisfied.
758 * @user_data: Data to pass to @callback.
760 * Asynchronously connects to an endpoint specified by @address and
761 * sets up the connection so it is in a state to run the client-side
762 * of the D-Bus authentication conversation.
764 * When the operation is finished, @callback will be invoked. You can
765 * then call g_dbus_address_get_stream_finish() to get the result of
768 * This is an asynchronous failable function. See
769 * g_dbus_address_get_stream_sync() for the synchronous version.
774 g_dbus_address_get_stream (const gchar *address,
775 GCancellable *cancellable,
776 GAsyncReadyCallback callback,
779 GSimpleAsyncResult *res;
782 g_return_if_fail (address != NULL);
784 res = g_simple_async_result_new (NULL,
787 g_dbus_address_get_stream);
788 data = g_new0 (GetStreamData, 1);
789 data->address = g_strdup (address);
790 g_simple_async_result_set_op_res_gpointer (res,
792 (GDestroyNotify) get_stream_data_free);
793 g_simple_async_result_run_in_thread (res,
794 get_stream_thread_func,
797 g_object_unref (res);
801 * g_dbus_address_get_stream_finish:
802 * @res: A #GAsyncResult obtained from the GAsyncReadyCallback passed to g_dbus_address_get_stream().
803 * @out_guid: %NULL or return location to store the GUID extracted from @address, if any.
804 * @error: Return location for error or %NULL.
806 * Finishes an operation started with g_dbus_address_get_stream().
808 * Returns: A #GIOStream or %NULL if @error is set.
813 g_dbus_address_get_stream_finish (GAsyncResult *res,
817 GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
821 g_return_val_if_fail (G_IS_ASYNC_RESULT (res), NULL);
822 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
824 g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == g_dbus_address_get_stream);
828 data = g_simple_async_result_get_op_res_gpointer (simple);
829 if (g_simple_async_result_propagate_error (simple, error))
832 ret = g_object_ref (data->stream);
833 if (out_guid != NULL)
834 *out_guid = g_strdup (data->guid);
841 * g_dbus_address_get_stream_sync:
842 * @address: A valid D-Bus address.
843 * @out_guid: %NULL or return location to store the GUID extracted from @address, if any.
844 * @cancellable: A #GCancellable or %NULL.
845 * @error: Return location for error or %NULL.
847 * Synchronously connects to an endpoint specified by @address and
848 * sets up the connection so it is in a state to run the client-side
849 * of the D-Bus authentication conversation.
851 * This is a synchronous failable function. See
852 * g_dbus_address_get_stream() for the asynchronous version.
854 * Returns: A #GIOStream or %NULL if @error is set.
859 g_dbus_address_get_stream_sync (const gchar *address,
861 GCancellable *cancellable,
869 g_return_val_if_fail (address != NULL, NULL);
870 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
875 addr_array = g_strsplit (address, ";", 0);
877 for (n = 0; addr_array != NULL && addr_array[n] != NULL; n++)
879 const gchar *addr = addr_array[n];
882 ret = g_dbus_address_try_connect_one (addr,
892 g_assert (this_error != NULL);
893 if (last_error != NULL)
894 g_error_free (last_error);
895 last_error = this_error;
902 if (last_error != NULL)
903 g_error_free (last_error);
907 g_assert (last_error != NULL);
908 g_propagate_error (error, last_error);
913 /* ---------------------------------------------------------------------------------------------------- */
915 /* TODO: implement for UNIX, Win32 and OS X */
917 get_session_address_platform_specific (void)
922 /* ---------------------------------------------------------------------------------------------------- */
925 * g_dbus_address_get_for_bus_sync:
926 * @bus_type: A #GBusType.
927 * @cancellable: A #GCancellable or %NULL.
928 * @error: Return location for error or %NULL.
930 * Synchronously looks up the D-Bus address for the well-known message
931 * bus instance specified by @bus_type. This may involve using various
932 * platform specific mechanisms.
934 * Returns: A valid D-Bus address string for @bus_type or %NULL if @error is set.
939 g_dbus_address_get_for_bus_sync (GBusType bus_type,
940 GCancellable *cancellable,
944 const gchar *starter_bus;
946 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
952 case G_BUS_TYPE_SYSTEM:
953 ret = g_strdup (g_getenv ("DBUS_SYSTEM_BUS_ADDRESS"));
956 ret = g_strdup ("unix:path=/var/run/dbus/system_bus_socket");
960 case G_BUS_TYPE_SESSION:
961 ret = g_strdup (g_getenv ("DBUS_SESSION_BUS_ADDRESS"));
964 ret = get_session_address_platform_specific ();
970 _("Cannot determine session bus address (TODO: run dbus-launch to find out)"));
975 case G_BUS_TYPE_STARTER:
976 starter_bus = g_getenv ("DBUS_STARTER_BUS_TYPE");
977 if (g_strcmp0 (starter_bus, "session") == 0)
979 ret = g_dbus_address_get_for_bus_sync (G_BUS_TYPE_SESSION, cancellable, error);
982 else if (g_strcmp0 (starter_bus, "system") == 0)
984 ret = g_dbus_address_get_for_bus_sync (G_BUS_TYPE_SYSTEM, cancellable, error);
989 if (starter_bus != NULL)
994 _("Cannot determine bus address from DBUS_STARTER_BUS_TYPE environment variable"
995 " - unknown value `%s'"),
1000 g_set_error_literal (error,
1003 _("Cannot determine bus address because the DBUS_STARTER_BUS_TYPE environment "
1004 "variable is not set"));
1013 _("Unknown bus type %d"),
1022 #define __G_DBUS_ADDRESS_C__
1023 #include "gioaliasdef.c"