+typedef struct
+{
+ GDBusServer *server;
+ GMainContext *context;
+ GMainLoop *loop;
+
+ GList *connections;
+} DmpData;
+
+static void
+dmp_data_free (DmpData *data)
+{
+ g_main_loop_unref (data->loop);
+ g_main_context_unref (data->context);
+ g_object_unref (data->server);
+ g_list_free_full (data->connections, g_object_unref);
+ g_free (data);
+}
+
+static void
+dmp_on_method_call (GDBusConnection *connection,
+ const gchar *sender,
+ const gchar *object_path,
+ const gchar *interface_name,
+ const gchar *method_name,
+ GVariant *parameters,
+ GDBusMethodInvocation *invocation,
+ gpointer user_data)
+{
+ //DmpData *data = user_data;
+ gint32 first;
+ gint32 second;
+ g_variant_get (parameters,
+ "(ii)",
+ &first,
+ &second);
+ g_dbus_method_invocation_return_value (invocation,
+ g_variant_new ("(i)", first + second));
+}
+
+static const GDBusInterfaceVTable dmp_interface_vtable =
+{
+ dmp_on_method_call,
+ NULL, /* get_property */
+ NULL /* set_property */
+};
+
+
+/* Runs in thread we created GDBusServer in (since we didn't pass G_DBUS_SERVER_FLAGS_RUN_IN_THREAD) */
+static gboolean
+dmp_on_new_connection (GDBusServer *server,
+ GDBusConnection *connection,
+ gpointer user_data)
+{
+ DmpData *data = user_data;
+ GDBusNodeInfo *node;
+ GError *error;
+
+ /* accept the connection */
+ data->connections = g_list_prepend (data->connections, g_object_ref (connection));
+
+ error = NULL;
+ node = g_dbus_node_info_new_for_xml ("<node>"
+ " <interface name='org.gtk.GDBus.DmpInterface'>"
+ " <method name='AddPair'>"
+ " <arg type='i' name='first' direction='in'/>"
+ " <arg type='i' name='second' direction='in'/>"
+ " <arg type='i' name='sum' direction='out'/>"
+ " </method>"
+ " </interface>"
+ "</node>",
+ &error);
+ g_assert_no_error (error);
+
+ /* sleep 100ms before exporting an object - this is to test that
+ * G_DBUS_CONNECTION_FLAGS_DELAY_MESSAGE_PROCESSING really works
+ * (GDBusServer uses this feature).
+ */
+ usleep (100 * 1000);
+
+ /* export an object */
+ error = NULL;
+ g_dbus_connection_register_object (connection,
+ "/dmp/test",
+ node->interfaces[0],
+ &dmp_interface_vtable,
+ data,
+ NULL,
+ &error);
+ g_dbus_node_info_unref (node);
+
+ return TRUE;
+}
+
+static gpointer
+dmp_thread_func (gpointer user_data)
+{
+ DmpData *data = user_data;
+ GError *error;
+ gchar *guid;
+
+ data->context = g_main_context_new ();
+ g_main_context_push_thread_default (data->context);
+
+ error = NULL;
+ guid = g_dbus_generate_guid ();
+ data->server = g_dbus_server_new_sync (tmp_address,
+ G_DBUS_SERVER_FLAGS_NONE,
+ guid,
+ NULL, /* GDBusAuthObserver */
+ NULL, /* GCancellable */
+ &error);
+ g_assert_no_error (error);
+ g_signal_connect (data->server,
+ "new-connection",
+ G_CALLBACK (dmp_on_new_connection),
+ data);
+
+ g_dbus_server_start (data->server);
+
+ data->loop = g_main_loop_new (data->context, FALSE);
+ g_main_loop_run (data->loop);
+
+ g_main_context_pop_thread_default (data->context);
+
+ g_free (guid);
+ return NULL;
+}
+
+static void
+delayed_message_processing (void)
+{
+ GError *error;
+ DmpData *data;
+ GThread *service_thread;
+ guint n;
+
+ data = g_new0 (DmpData, 1);
+
+ service_thread = g_thread_new ("dmp",
+ dmp_thread_func,
+ data);
+ while (data->server == NULL || !g_dbus_server_is_active (data->server))
+ g_thread_yield ();
+
+ for (n = 0; n < 5; n++)
+ {
+ GDBusConnection *c;
+ GVariant *res;
+ gint32 val;
+
+ error = NULL;
+ c = g_dbus_connection_new_for_address_sync (g_dbus_server_get_client_address (data->server),
+ G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT,
+ NULL, /* GDBusAuthObserver */
+ NULL, /* GCancellable */
+ &error);
+ g_assert_no_error (error);
+
+ error = NULL;
+ res = g_dbus_connection_call_sync (c,
+ NULL, /* bus name */
+ "/dmp/test",
+ "org.gtk.GDBus.DmpInterface",
+ "AddPair",
+ g_variant_new ("(ii)", 2, n),
+ G_VARIANT_TYPE ("(i)"),
+ G_DBUS_CALL_FLAGS_NONE,
+ -1, /* timeout_msec */
+ NULL, /* GCancellable */
+ &error);
+ g_assert_no_error (error);
+ g_variant_get (res, "(i)", &val);
+ g_assert_cmpint (val, ==, 2 + n);
+ g_variant_unref (res);
+ g_object_unref (c);
+ }
+
+ g_main_loop_quit (data->loop);
+ g_thread_join (service_thread);
+ dmp_data_free (data);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static gboolean
+nonce_tcp_on_authorize_authenticated_peer (GDBusAuthObserver *observer,
+ GIOStream *stream,
+ GCredentials *credentials,
+ gpointer user_data)
+{
+ PeerData *data = user_data;
+ gboolean authorized;
+
+ data->num_connection_attempts++;
+
+ authorized = TRUE;
+ if (!data->accept_connection)
+ {
+ authorized = FALSE;
+ g_main_loop_quit (loop);
+ }
+
+ return authorized;
+}
+
+/* Runs in thread we created GDBusServer in (since we didn't pass G_DBUS_SERVER_FLAGS_RUN_IN_THREAD) */
+static gboolean
+nonce_tcp_on_new_connection (GDBusServer *server,
+ GDBusConnection *connection,
+ gpointer user_data)
+{
+ PeerData *data = user_data;
+
+ g_ptr_array_add (data->current_connections, g_object_ref (connection));
+
+ g_main_loop_quit (loop);
+
+ return TRUE;
+}
+
+static gpointer
+nonce_tcp_service_thread_func (gpointer user_data)
+{
+ PeerData *data = user_data;
+ GMainContext *service_context;
+ GDBusAuthObserver *observer;
+ GError *error;
+
+ service_context = g_main_context_new ();
+ g_main_context_push_thread_default (service_context);
+
+ error = NULL;
+ observer = g_dbus_auth_observer_new ();
+ server = g_dbus_server_new_sync ("nonce-tcp:",
+ G_DBUS_SERVER_FLAGS_NONE,
+ test_guid,
+ observer,
+ NULL, /* cancellable */
+ &error);
+ g_assert_no_error (error);
+
+ g_signal_connect (server,
+ "new-connection",
+ G_CALLBACK (nonce_tcp_on_new_connection),
+ data);
+ g_signal_connect (observer,
+ "authorize-authenticated-peer",
+ G_CALLBACK (nonce_tcp_on_authorize_authenticated_peer),
+ data);
+ g_object_unref (observer);
+
+ g_dbus_server_start (server);
+
+ create_service_loop (service_context);
+ g_main_loop_run (service_loop);
+
+ g_main_context_pop_thread_default (service_context);
+
+ teardown_service_loop ();
+ g_main_context_unref (service_context);
+
+ /* test code specifically unrefs the server - see below */
+ g_assert (server == NULL);
+
+ return NULL;
+}
+
+static void
+test_nonce_tcp (void)
+{
+ PeerData data;
+ GError *error;
+ GThread *service_thread;
+ GDBusConnection *c;
+ gchar *s;
+ gchar *nonce_file;
+ gboolean res;
+ const gchar *address;
+
+ memset (&data, '\0', sizeof (PeerData));
+ data.current_connections = g_ptr_array_new_with_free_func (g_object_unref);
+
+ error = NULL;
+ server = NULL;
+ service_thread = g_thread_new ("nonce-tcp-service",
+ nonce_tcp_service_thread_func,
+ &data);
+ await_service_loop ();
+ g_assert (server != NULL);
+
+ /* bring up a connection and accept it */
+ data.accept_connection = TRUE;
+ error = NULL;
+ c = g_dbus_connection_new_for_address_sync (g_dbus_server_get_client_address (server),
+ G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT,
+ NULL, /* GDBusAuthObserver */
+ NULL, /* cancellable */
+ &error);
+ g_assert_no_error (error);
+ g_assert (c != NULL);
+ while (data.current_connections->len < 1)
+ g_thread_yield ();
+ g_assert_cmpint (data.current_connections->len, ==, 1);
+ g_assert_cmpint (data.num_connection_attempts, ==, 1);
+ g_assert (g_dbus_connection_get_unique_name (c) == NULL);
+ g_assert_cmpstr (g_dbus_connection_get_guid (c), ==, test_guid);
+ g_object_unref (c);
+
+ /* now, try to subvert the nonce file (this assumes noncefile is the last key/value pair)
+ */
+
+ address = g_dbus_server_get_client_address (server);
+
+ s = strstr (address, "noncefile=");
+ g_assert (s != NULL);
+ s += sizeof "noncefile=" - 1;
+ nonce_file = g_strdup (s);
+
+ /* First try invalid data in the nonce file - this will actually
+ * make the client send this and the server will reject it. The way
+ * it works is that if the nonce doesn't match, the server will
+ * simply close the connection. So, from the client point of view,
+ * we can see a variety of errors.
+ */
+ error = NULL;
+ res = g_file_set_contents (nonce_file,
+ "0123456789012345",
+ -1,
+ &error);
+ g_assert_no_error (error);
+ g_assert (res);
+ c = g_dbus_connection_new_for_address_sync (address,
+ G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT,
+ NULL, /* GDBusAuthObserver */
+ NULL, /* cancellable */
+ &error);
+ _g_assert_error_domain (error, G_IO_ERROR);
+ g_error_free (error);
+ g_assert (c == NULL);
+
+ /* Then try with a nonce-file of incorrect length - this will make
+ * the client complain - we won't even try connecting to the server
+ * for this
+ */
+ error = NULL;
+ res = g_file_set_contents (nonce_file,
+ "0123456789012345_",
+ -1,
+ &error);
+ g_assert_no_error (error);
+ g_assert (res);
+ c = g_dbus_connection_new_for_address_sync (address,
+ G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT,
+ NULL, /* GDBusAuthObserver */
+ NULL, /* cancellable */
+ &error);
+ g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
+ g_error_free (error);
+ g_assert (c == NULL);
+
+ /* Finally try with no nonce-file at all */
+ g_assert_cmpint (g_unlink (nonce_file), ==, 0);
+ error = NULL;
+ c = g_dbus_connection_new_for_address_sync (address,
+ G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT,
+ NULL, /* GDBusAuthObserver */
+ NULL, /* cancellable */
+ &error);
+ g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
+ g_error_free (error);
+ g_assert (c == NULL);
+
+ g_free (nonce_file);
+
+ g_dbus_server_stop (server);
+ g_object_unref (server);
+ server = NULL;
+
+ g_main_loop_quit (service_loop);
+ g_thread_join (service_thread);
+}
+
+static void
+test_credentials (void)
+{
+ GCredentials *c1, *c2;
+ GError *error;
+ gchar *desc;
+
+ c1 = g_credentials_new ();
+ c2 = g_credentials_new ();
+
+ error = NULL;
+ if (g_credentials_set_unix_user (c2, getuid (), &error))
+ g_assert_no_error (error);
+
+ g_clear_error (&error);
+ g_assert (g_credentials_is_same_user (c1, c2, &error));
+ g_assert_no_error (error);
+
+ desc = g_credentials_to_string (c1);
+ g_assert (desc != NULL);
+ g_free (desc);
+
+ g_object_unref (c1);
+ g_object_unref (c2);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static gboolean
+tcp_anonymous_on_new_connection (GDBusServer *server,
+ GDBusConnection *connection,
+ gpointer user_data)
+{
+ gboolean *seen_connection = user_data;
+ *seen_connection = TRUE;
+ return TRUE;
+}
+
+static gpointer
+tcp_anonymous_service_thread_func (gpointer user_data)
+{
+ gboolean *seen_connection = user_data;
+ GMainContext *service_context;
+ GError *error;
+
+ service_context = g_main_context_new ();
+ g_main_context_push_thread_default (service_context);
+
+ error = NULL;
+ server = g_dbus_server_new_sync ("tcp:",
+ G_DBUS_SERVER_FLAGS_AUTHENTICATION_ALLOW_ANONYMOUS,
+ test_guid,
+ NULL, /* GDBusObserver* */
+ NULL, /* GCancellable* */
+ &error);
+ g_assert_no_error (error);
+
+ g_signal_connect (server,
+ "new-connection",
+ G_CALLBACK (tcp_anonymous_on_new_connection),
+ seen_connection);
+
+ g_dbus_server_start (server);
+
+ create_service_loop (service_context);
+ g_main_loop_run (service_loop);
+
+ g_main_context_pop_thread_default (service_context);
+
+ teardown_service_loop ();
+ g_main_context_unref (service_context);
+
+ return NULL;
+}
+
+static void
+test_tcp_anonymous (void)
+{
+ gboolean seen_connection;
+ GThread *service_thread;
+ GDBusConnection *connection;
+ GError *error;
+
+ seen_connection = FALSE;
+ service_thread = g_thread_new ("tcp-anon-service",
+ tcp_anonymous_service_thread_func,
+ &seen_connection);
+ await_service_loop ();
+ g_assert (server != NULL);
+
+ error = NULL;
+ connection = g_dbus_connection_new_for_address_sync (g_dbus_server_get_client_address (server),
+ G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT,
+ NULL, /* GDBusAuthObserver* */
+ NULL, /* GCancellable */
+ &error);
+ g_assert_no_error (error);
+ g_assert (connection != NULL);
+
+ while (!seen_connection)
+ g_thread_yield ();
+
+ g_object_unref (connection);
+
+ g_main_loop_quit (service_loop);
+ g_dbus_server_stop (server);
+ g_object_unref (server);
+ server = NULL;
+
+ g_thread_join (service_thread);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static GDBusServer *codegen_server = NULL;
+
+static gboolean
+codegen_on_animal_poke (ExampleAnimal *animal,
+ GDBusMethodInvocation *invocation,
+ gboolean make_sad,
+ gboolean make_happy,
+ gpointer user_data)
+{
+ if ((make_sad && make_happy) || (!make_sad && !make_happy))
+ {
+ g_main_loop_quit (service_loop);
+
+ g_dbus_method_invocation_return_dbus_error (invocation,
+ "org.gtk.GDBus.Examples.ObjectManager.Error.Failed",
+ "Exactly one of make_sad or make_happy must be TRUE");
+ goto out;
+ }
+
+ if (make_sad)
+ {
+ if (g_strcmp0 (example_animal_get_mood (animal), "Sad") == 0)
+ {
+ g_dbus_method_invocation_return_dbus_error (invocation,
+ "org.gtk.GDBus.Examples.ObjectManager.Error.SadAnimalIsSad",
+ "Sad animal is already sad");
+ goto out;
+ }
+
+ example_animal_set_mood (animal, "Sad");
+ example_animal_complete_poke (animal, invocation);
+ goto out;
+ }
+
+ if (make_happy)
+ {
+ if (g_strcmp0 (example_animal_get_mood (animal), "Happy") == 0)
+ {
+ g_dbus_method_invocation_return_dbus_error (invocation,
+ "org.gtk.GDBus.Examples.ObjectManager.Error.HappyAnimalIsHappy",
+ "Happy animal is already happy");
+ goto out;
+ }
+
+ example_animal_set_mood (animal, "Happy");
+ example_animal_complete_poke (animal, invocation);
+ goto out;
+ }
+
+ g_assert_not_reached ();
+
+ out:
+ return TRUE; /* to indicate that the method was handled */
+}
+
+/* Runs in thread we created GDBusServer in (since we didn't pass G_DBUS_SERVER_FLAGS_RUN_IN_THREAD) */
+static gboolean
+codegen_on_new_connection (GDBusServer *server,
+ GDBusConnection *connection,
+ gpointer user_data)
+{
+ ExampleAnimal *animal = user_data;
+ GError *error = NULL;
+
+ /* g_print ("Client connected.\n" */
+ /* "Negotiated capabilities: unix-fd-passing=%d\n", */
+ /* g_dbus_connection_get_capabilities (connection) & G_DBUS_CAPABILITY_FLAGS_UNIX_FD_PASSING); */
+
+ g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (animal), connection,
+ "/Example/Animals/000", &error);
+ g_assert_no_error (error);
+
+ return TRUE;
+}
+
+static gpointer
+codegen_service_thread_func (gpointer user_data)
+{
+ GMainContext *service_context;
+ ExampleAnimal *animal;
+ GError *error = NULL;
+
+ service_context = g_main_context_new ();
+ g_main_context_push_thread_default (service_context);
+
+ /* Create the animal in the right thread context */
+ animal = example_animal_skeleton_new ();
+
+ /* Handle Poke() D-Bus method invocations on the .Animal interface */
+ g_signal_connect (animal, "handle-poke",
+ G_CALLBACK (codegen_on_animal_poke),
+ NULL); /* user_data */
+
+ codegen_server = g_dbus_server_new_sync (tmp_address,
+ G_DBUS_SERVER_FLAGS_NONE,
+ test_guid,
+ NULL, /* observer */
+ NULL, /* cancellable */
+ &error);
+ g_assert_no_error (error);
+ g_dbus_server_start (codegen_server);
+
+ g_signal_connect (codegen_server, "new-connection",
+ G_CALLBACK (codegen_on_new_connection),
+ animal);
+
+ create_service_loop (service_context);
+ g_main_loop_run (service_loop);
+
+ g_object_unref (animal);
+
+ g_main_context_pop_thread_default (service_context);
+
+ teardown_service_loop ();
+ g_main_context_unref (service_context);
+
+ g_dbus_server_stop (codegen_server);
+ g_object_unref (codegen_server);
+ codegen_server = NULL;
+
+ return NULL;
+}
+
+
+static gboolean
+codegen_quit_mainloop_timeout (gpointer data)
+{
+ g_main_loop_quit (loop);
+ return FALSE;
+}
+
+static void
+codegen_test_peer (void)
+{
+ GDBusConnection *connection;
+ ExampleAnimal *animal1, *animal2;
+ GThread *service_thread;
+ GError *error = NULL;
+ GVariant *value;
+ const gchar *s;
+
+ /* bring up a server - we run the server in a different thread to avoid deadlocks */
+ service_thread = g_thread_new ("codegen_test_peer",
+ codegen_service_thread_func,
+ NULL);
+ await_service_loop ();
+ g_assert (codegen_server != NULL);
+
+ /* Get an animal 1 ... */
+ connection = g_dbus_connection_new_for_address_sync (g_dbus_server_get_client_address (codegen_server),
+ G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT,
+ NULL, /* GDBusAuthObserver */
+ NULL, /* cancellable */
+ &error);
+ g_assert_no_error (error);
+ g_assert (connection != NULL);
+
+ animal1 = example_animal_proxy_new_sync (connection, 0, NULL,
+ "/Example/Animals/000", NULL, &error);
+ g_assert_no_error (error);
+ g_assert (animal1 != NULL);
+ g_object_unref (connection);
+
+ /* Get animal 2 ... */
+ connection = g_dbus_connection_new_for_address_sync (g_dbus_server_get_client_address (codegen_server),
+ G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT,
+ NULL, /* GDBusAuthObserver */
+ NULL, /* cancellable */
+ &error);
+ g_assert_no_error (error);
+ g_assert (connection != NULL);
+
+ animal2 = example_animal_proxy_new_sync (connection, 0, NULL,
+ "/Example/Animals/000", NULL, &error);
+ g_assert_no_error (error);
+ g_assert (animal2 != NULL);
+ g_object_unref (connection);
+
+ /* Make animal sad via animal1 */
+ example_animal_call_poke_sync (animal1, TRUE, FALSE, NULL, &error);
+ g_assert_no_error (error);
+
+ /* Poke server and make sure animal is updated */
+ value = g_dbus_proxy_call_sync (G_DBUS_PROXY (animal1),
+ "org.freedesktop.DBus.Peer.Ping",
+ NULL, G_DBUS_CALL_FLAGS_NONE, -1,
+ NULL, &error);
+ g_assert_no_error (error);
+ g_assert (value != NULL);
+ g_variant_unref (value);
+
+ /* Give the proxies a chance to refresh in the defaul main loop */
+ g_timeout_add (100, codegen_quit_mainloop_timeout, NULL);
+ g_main_loop_run (loop);
+
+ /* Assert animals are sad */
+ g_assert_cmpstr (example_animal_get_mood (animal1), ==, "Sad");
+ g_assert_cmpstr (example_animal_get_mood (animal2), ==, "Sad");
+
+ /* Make animal happy via animal2 */
+ example_animal_call_poke_sync (animal2, FALSE, TRUE, NULL, &error);
+ g_assert_no_error (error);
+
+ /* Some random unrelated call, just to get some test coverage */
+ value = g_dbus_proxy_call_sync (G_DBUS_PROXY (animal2),
+ "org.freedesktop.DBus.Peer.GetMachineId",
+ NULL, G_DBUS_CALL_FLAGS_NONE, -1,
+ NULL, &error);
+ g_assert_no_error (error);
+ g_variant_get (value, "(&s)", &s);
+ g_assert (g_dbus_is_guid (s));
+ g_variant_unref (value);
+
+ /* Poke server and make sure animal is updated */
+ value = g_dbus_proxy_call_sync (G_DBUS_PROXY (animal2),
+ "org.freedesktop.DBus.Peer.Ping",
+ NULL, G_DBUS_CALL_FLAGS_NONE, -1,
+ NULL, &error);
+ g_assert_no_error (error);
+ g_assert (value != NULL);
+ g_variant_unref (value);
+
+ /* Give the proxies a chance to refresh in the defaul main loop */
+ g_timeout_add (1000, codegen_quit_mainloop_timeout, NULL);
+ g_main_loop_run (loop);
+
+ /* Assert animals are happy */
+ g_assert_cmpstr (example_animal_get_mood (animal1), ==, "Happy");
+ g_assert_cmpstr (example_animal_get_mood (animal2), ==, "Happy");
+
+ /* This final call making the animal happy and sad will cause
+ * the server to quit, when the server quits we dont get property
+ * change notifications anyway because those are done from an idle handler
+ */
+ example_animal_call_poke_sync (animal2, TRUE, TRUE, NULL, &error);
+
+ g_object_unref (animal1);
+ g_object_unref (animal2);
+ g_thread_join (service_thread);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+