diff options
author | Simon McVittie <simon.mcvittie@collabora.co.uk> | 2012-04-10 13:51:53 +0100 |
---|---|---|
committer | Simon McVittie <simon.mcvittie@collabora.co.uk> | 2012-04-10 13:51:53 +0100 |
commit | 7238bf9925a4bf62bec867af49f5f82e4f6bf153 (patch) | |
tree | f763c11515d06f6d02ff843d43582a65aff696f3 | |
parent | d55fb31b091a5d2bc0d1664378e8a5a0230c8c83 (diff) | |
parent | b0919cddbde87259613b0f9c56fb9f25b6065b12 (diff) |
Merge branch 'master' into next
Conflicts:
configure.ac
examples/client/stream-tubes/Makefile.am
telepathy-glib/Makefile.am
telepathy-glib/abi.am
telepathy-glib/automatic-client-factory.c
telepathy-glib/base-contact-list-internal.h
telepathy-glib/base-contact-list.c
telepathy-glib/call-stream.c
telepathy-glib/channel.c
telepathy-glib/connection.c
telepathy-glib/introspection.am
tests/dbus/account-manager.c
tests/dbus/contacts.c
tests/lib/contact-list-manager.c
tests/lib/contacts-conn.c
tests/lib/simple-account-manager.c
tests/lib/simple-account-manager.h
62 files changed, 3539 insertions, 375 deletions
diff --git a/.gitignore b/.gitignore index 0d755a573..546bff798 100644 --- a/.gitignore +++ b/.gitignore @@ -55,6 +55,8 @@ examples/client/telepathy-example-* examples/cm/*/telepathy-example-* examples/extensions/extensions.html /examples/future/*/telepathy-example-* +examples/client/dbus-tubes/accepter +examples/client/dbus-tubes/offerer examples/client/stream-tubes/accepter examples/client/stream-tubes/offerer /extensions/extensions.html diff --git a/Android.mk b/Android.mk index bbd2f520d..4cab566bb 100644 --- a/Android.mk +++ b/Android.mk @@ -26,10 +26,6 @@ telepathy-glib-configure-real: for file in $(TELEPATHY_GLIB_BUILT_SOURCES); do \ rm -f $$file && \ make -C $$(dirname $$file) $$(basename $$file) ; \ - done && \ - for file in $(TELEPATHY_GLIB_GENMARSHAL); do \ - rm -f telepathy-glib/$$file && \ - make -C telepathy-glib $$file ; \ done telepathy-glib-configure: telepathy-glib-configure-real diff --git a/Makefile.am b/Makefile.am index 086eff19c..1948f352a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -48,7 +48,7 @@ upload-branch-docs: all include tools/lcov.am -CHANGELOG_RANGE = telepathy-glib-0.12.0.. +CHANGELOG_RANGE = telepathy-glib-0.14.0.. CHECK_FOR_UNRELEASED = $(srcdir)/NEWS $(wildcard $(srcdir)/telepathy-glib/*.[ch]) include tools/telepathy.am @@ -1,4 +1,65 @@ -telepathy-glib 0.17.6 (UNRELEASED) +telepathy-glib 0.19.0 (UNRELEASED) +================================== + +... + +telepathy-glib 0.18.0 (2012-04-02) +================================== + +This is the start of a new stable branch. We encourage those shipping +GNOME 3.4 to track this stable branch. + +Changes since 0.17.7: + +• Support the DownloadAtConnection property in TpBaseContactList. (Alban) + +• Add high-level API for TpDBusTubeChannel classes. (fd.o#29271, + Guillaume and Will) + +• Various improvements to the ContactList test suite. (Xavier) + +Summary of particularly noteworthy changes since 0.16.x: + +• GLib ≥ 2.30, dbus-glib ≥ 0.90, gobject-introspection ≥ 1.30, and + valac ≥ 0.14 are now required. + +• TpCallChannel, TpBaseCallChannel, and other Call-related high-level + API has been added. + +• High-level API to accept and provide file transfers has been added. + +• Building on Android using 'androgenizer' is now supported. + +telepathy-glib 0.17.7 (2012-03-22) +================================== + +API additions: + +• …get_local_sending on TpBaseMediaCallStream exposes the local + sending state. (Olivier) + +• TpCallStateReason structure now has a message string + member. (Olivier) + +Fixes: + +• TpBaseMediaCallContent: be sure to update the local sending state on + call acceptance. (Olivier) + +• A few miscellaneous fixes to the Call1 code, including: + • ensuring local sending state is updated on call acceptance, + • ignoring sending/receiving failures while held, + • only emitting STUNServersChanged when they have actually + changed, + • and not waiting for streams to start again after holding if they + weren't sending before. (Olivier) + +Enhancements: + +• Use close_channels_async on the channel dispatch operation in the + example approver. (Will) + +telepathy-glib 0.17.6 (19/03/2012) ================================== Requirements: @@ -31,6 +92,11 @@ Enhancements: • Add accessors for TpAccount properties, parameters and storage identifier represented as a GVariant (fd.o #30422, Simon) +• TpCallChannel: add API to put the call on hold. (Olivier) + +• TpCallContentMediaDescription now implements the RTPHeaderExtensions, + RTCPFeedback and RTCPExtendedReports interfaces. (Olivier) + Fixes: • Don't change the direction of Call streams because of a Hold (Olivier) @@ -56,6 +122,8 @@ Fixes: • Improve test coverage for the connection manager test (fd.o #46358, Simon) +• TpCallChannel::state-changed is properly annotate (fdo.o #47410 Guillaume) + telepathy-glib 0.17.5 (2012-02-20) ================================== diff --git a/configure.ac b/configure.ac index ba3bafa43..997c9f0c7 100644 --- a/configure.ac +++ b/configure.ac @@ -332,6 +332,7 @@ AC_OUTPUT( Makefile \ examples/client/js/Makefile \ examples/client/python/Makefile \ examples/client/stream-tubes/Makefile \ + examples/client/dbus-tubes/Makefile \ examples/cm/Makefile \ examples/cm/call/Makefile \ examples/cm/channelspecific/Makefile \ diff --git a/docs/reference/telepathy-glib-sections.txt b/docs/reference/telepathy-glib-sections.txt index 7ecc73af9..ecbe6d385 100644 --- a/docs/reference/telepathy-glib-sections.txt +++ b/docs/reference/telepathy-glib-sections.txt @@ -4925,7 +4925,11 @@ tp_base_contact_list_dup_contacts TpBaseContactListDupStatesFunc tp_base_contact_list_dup_states TpBaseContactListUIntFunc +TpBaseContactListAsyncFunc TpBaseContactListAsyncFinishFunc +tp_base_contact_list_download_async +tp_base_contact_list_download_finish +tp_base_contact_list_get_download_at_connection <SUBSECTION changes> TP_TYPE_MUTABLE_CONTACT_LIST TpMutableContactListInterface @@ -5072,8 +5076,13 @@ TpStreamTubeConnectionPrivate <SUBSECTION> TpDBusTubeChannel TpDBusTubeChannelClass +TP_DBUS_TUBE_CHANNEL_FEATURE_CORE tp_dbus_tube_channel_get_parameters tp_dbus_tube_channel_get_service_name +tp_dbus_tube_channel_offer_async +tp_dbus_tube_channel_offer_finish +tp_dbus_tube_channel_accept_async +tp_dbus_tube_channel_accept_finish <SUBSECTION Standard> TP_IS_DBUS_TUBE_CHANNEL TP_IS_DBUS_TUBE_CHANNEL_CLASS @@ -5083,6 +5092,7 @@ TP_DBUS_TUBE_CHANNEL_GET_CLASS TP_TYPE_DBUS_TUBE_CHANNEL tp_dbus_tube_channel_get_type TpDBusTubeChannelPrivate +tp_dbus_tube_channel_feature_quark_core </SECTION> <SECTION> @@ -5421,6 +5431,7 @@ tp_call_channel_has_mutable_contents tp_call_channel_get_members <SUBSECTION> tp_call_channel_has_dtmf +tp_call_channel_has_hold tp_call_channel_send_tones_async tp_call_channel_send_tones_finish <SUBSECTION> @@ -5434,6 +5445,8 @@ tp_call_channel_hangup_async tp_call_channel_hangup_finish tp_call_channel_add_content_async tp_call_channel_add_content_finish +tp_call_channel_request_hold_async +tp_call_channel_request_hold_finish <SUBSECTION Standard> TpCallChannelPrivate tp_call_channel_get_type @@ -5861,6 +5874,14 @@ tp_call_content_media_description_get_object_path tp_call_content_media_description_get_remote_contact tp_call_content_media_description_append_codec tp_call_content_media_description_add_ssrc +tp_call_content_media_description_add_rtp_header_extension +tp_call_content_media_description_add_rtcp_feedback_message +tp_call_content_media_description_set_rtcp_feedback_minimum_interval +tp_call_content_media_description_set_does_avpf +tp_call_content_media_description_set_rtcp_extended_reports +tp_call_content_media_description_add_rtcp_extended_reports_interface +tp_call_content_media_description_add_rtcp_feedback_interface +tp_call_content_media_description_add_rtp_header_extensions_interface <SUBSECTION Standard> TP_CALL_CONTENT_MEDIA_DESCRIPTION TP_CALL_CONTENT_MEDIA_DESCRIPTION_CLASS @@ -5897,6 +5918,7 @@ tp_base_media_call_stream_get_receiving_state tp_base_media_call_stream_update_sending_state tp_base_media_call_stream_get_sending_state tp_base_media_call_stream_set_local_sending +tp_base_media_call_stream_get_local_sending tp_base_media_call_stream_get_local_candidates <SUBSECTION Standard> TP_BASE_MEDIA_CALL_STREAM @@ -5944,6 +5966,7 @@ TpBaseMediaCallChannel TpBaseMediaCallChannelClass TpBaseMediaCallChannelHoldStateChangedFunc TpBaseMediaCallChannelVoidFunc +tp_base_media_call_channel_get_local_hold_state <SUBSECTION Standard> TP_BASE_MEDIA_CALL_CHANNEL TP_BASE_MEDIA_CALL_CHANNEL_CLASS diff --git a/examples/client/Makefile.am b/examples/client/Makefile.am index 1f73801a3..9e0987b00 100644 --- a/examples/client/Makefile.am +++ b/examples/client/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS = stream-tubes python js +SUBDIRS = stream-tubes dbus-tubes python js EXAMPLES = diff --git a/examples/client/approver.c b/examples/client/approver.c index ec54fbaa5..85156a1f7 100644 --- a/examples/client/approver.c +++ b/examples/client/approver.c @@ -52,32 +52,22 @@ handle_with_cb (GObject *source, } static void -claim_cb (GObject *source, +close_cb (GObject *source, GAsyncResult *result, gpointer user_data) { TpChannelDispatchOperation *cdo = TP_CHANNEL_DISPATCH_OPERATION (source); GError *error; - GPtrArray *channels; - guint i; - if (!tp_channel_dispatch_operation_claim_with_finish (cdo, result, &error)) + if (!tp_channel_dispatch_operation_close_channels_finish (cdo, result, &error)) { - g_print ("Claim() failed: %s\n", error->message); + g_print ("Rejecting channels failed: %s\n", error->message); g_error_free (error); return; } - g_print ("Claim() succeeded, close channels\n"); - - channels = tp_channel_dispatch_operation_borrow_channels (cdo); - for (i = 0; i < channels->len; i++) - { - TpChannel *channel = g_ptr_array_index (channels, i); - - tp_cli_channel_call_close (channel, -1, NULL, NULL, NULL, NULL); - } + g_print ("Rejected all the things!\n"); } @@ -138,10 +128,9 @@ add_dispatch_operation_cb (TpSimpleApprover *self, } else if (c == 'n' || c == 'N') { - g_print ("Dissaprove channels\n"); + g_print ("Reject channels\n"); - tp_channel_dispatch_operation_claim_with_async (cdo, - TP_BASE_CLIENT (self), claim_cb, NULL); + tp_channel_dispatch_operation_close_channels_async (cdo, close_cb, NULL); } else { diff --git a/examples/client/dbus-tubes/Makefile.am b/examples/client/dbus-tubes/Makefile.am new file mode 100644 index 000000000..d30b83b79 --- /dev/null +++ b/examples/client/dbus-tubes/Makefile.am @@ -0,0 +1,25 @@ +noinst_PROGRAMS = \ + offerer \ + accepter \ + $(NULL) + +offerer_SOURCES = offerer.c constants.h +accepter_SOURCES = accepter.c constants.h + +# In an external project you'd use $(TP_GLIB_LIBS) (obtained from +# pkg-config via autoconf) instead of the .la path +LDADD = \ + $(top_builddir)/telepathy-glib/libtelepathy-glib-1.la \ + $(top_builddir)/telepathy-glib/libtelepathy-glib-1-dbus.la \ + $(top_builddir)/telepathy-glib/libtelepathy-glib-1-core.la \ + @DBUS_LIBS@ \ + @GLIB_LIBS@ + +AM_CFLAGS = \ + $(ERROR_CFLAGS) \ + @DBUS_CFLAGS@ \ + @GLIB_CFLAGS@ \ + @TP_GLIB_CFLAGS@ +AM_LDFLAGS = \ + $(ERROR_LDFLAGS) \ + $(NULL) diff --git a/examples/client/dbus-tubes/accepter.c b/examples/client/dbus-tubes/accepter.c new file mode 100644 index 000000000..638c536d7 --- /dev/null +++ b/examples/client/dbus-tubes/accepter.c @@ -0,0 +1,220 @@ +#include <telepathy-glib/telepathy-glib.h> +#include "constants.h" + +static GMainLoop *loop = NULL; + +static void +dbus_connection_closed_cb ( + GDBusConnection *connection, + gboolean remote_peer_vanished, + GError *error, + gpointer user_data) +{ + if (remote_peer_vanished) + g_debug ("remote peer disconnected: %s", error->message); + else if (error != NULL) + g_debug ("remote peer sent broken data: %s", error->message); + else + g_debug ("supposedly we closed the connection locally?!"); + + g_object_unref (connection); +} + +static void +lucky_number_cb ( + GDBusConnection *connection, + const gchar *sender_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *signal_name, + GVariant *parameters, + gpointer user_data) +{ + if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(u)"))) + { + guint32 x; + + g_variant_get (parameters, "(u)", &x); + g_debug ("My lucky number is: %u", x); + } + else + { + g_warning ("LuckyNumber's arguments were %s, not (u)", + g_variant_get_type_string (parameters)); + } +} + +static void +add_cb ( + GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + GDBusConnection *conn = G_DBUS_CONNECTION (source); + GVariant *ret; + GError *error = NULL; + + ret = g_dbus_connection_call_finish (conn, result, &error); + + if (ret != NULL) + { + gint32 value; + + g_variant_get (ret, "(i)", &value); + g_debug ("Adding my numbers together gave: %i", value); + g_variant_unref (ret); + } + else + { + g_warning ("Add() failed: %s", error->message); + g_clear_error (&error); + } +} + +static void +tube_accepted (GObject *tube, + GAsyncResult *res, + gpointer user_data) +{ + GDBusConnection *conn; + GError *error = NULL; + + conn = tp_dbus_tube_channel_accept_finish ( + TP_DBUS_TUBE_CHANNEL (tube), res, &error); + if (conn == NULL) + { + g_debug ("Failed to accept tube: %s", error->message); + g_error_free (error); + tp_channel_close_async (TP_CHANNEL (tube), NULL, NULL); + return; + } + + g_debug ("tube accepted"); + g_signal_connect (conn, "closed", + G_CALLBACK (dbus_connection_closed_cb), NULL); + + g_dbus_connection_signal_subscribe (conn, + /* since we only deal with 1-1 connections, no need to match sender */ + NULL, + EXAMPLE_INTERFACE, + "LuckyNumber", + EXAMPLE_PATH, + NULL, + G_DBUS_SIGNAL_FLAGS_NONE, + lucky_number_cb, + NULL, NULL); + g_dbus_connection_call (conn, + NULL, + EXAMPLE_PATH, + EXAMPLE_INTERFACE, + "Add", + g_variant_new ("(ii)", 45, 54), + G_VARIANT_TYPE ("(i)"), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + add_cb, + NULL); +} + +static void +tube_invalidated_cb (TpStreamTubeChannel *tube, + guint domain, + gint code, + gchar *message, + gpointer user_data) +{ + g_debug ("Tube has been invalidated: %s", message); + g_main_loop_quit (loop); + g_object_unref (tube); +} + +static void +handle_channels (TpSimpleHandler *handler, + TpAccount *account, + TpConnection *conn, + GList *channels, + GList *requests, + gint64 action_time, + TpHandleChannelsContext *context, + gpointer user_data) +{ + TpDBusTubeChannel *tube; + GList *l; + GError error = { TP_ERRORS, TP_ERROR_NOT_AVAILABLE, + "No channel to be handled" }; + + g_debug ("Handling channels"); + + for (l = channels; l != NULL; l = l->next) + { + TpDBusTubeChannel *channel = l->data; + + if (!TP_IS_DBUS_TUBE_CHANNEL (channel)) + continue; + + if (tp_strdiff (tp_dbus_tube_channel_get_service_name (channel), + EXAMPLE_SERVICE_NAME)) + continue; + + g_debug ("Accepting tube"); + + tube = g_object_ref (channel); + + g_signal_connect (tube, "invalidated", + G_CALLBACK (tube_invalidated_cb), NULL); + + tp_dbus_tube_channel_accept_async (tube, tube_accepted, context); + + tp_handle_channels_context_accept (context); + return; + } + + g_debug ("Rejecting channels"); + tp_handle_channels_context_fail (context, &error); +} + + +int +main (int argc, + const char **argv) +{ + TpAccountManager *manager; + TpBaseClient *handler; + GError *error = NULL; + + g_type_init (); + + manager = tp_account_manager_dup (); + handler = tp_simple_handler_new_with_am (manager, FALSE, FALSE, + "ExampleServiceHandler", FALSE, handle_channels, NULL, NULL); + + tp_base_client_take_handler_filter (handler, tp_asv_new ( + TP_PROP_CHANNEL_CHANNEL_TYPE, + G_TYPE_STRING, + TP_IFACE_CHANNEL_TYPE_DBUS_TUBE, + + TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, + G_TYPE_UINT, + TP_HANDLE_TYPE_CONTACT, + + TP_PROP_CHANNEL_TYPE_DBUS_TUBE_SERVICE_NAME, + G_TYPE_STRING, + EXAMPLE_SERVICE_NAME, + + NULL)); + + tp_base_client_register (handler, &error); + g_assert_no_error (error); + + g_debug ("Waiting for tube offer"); + + loop = g_main_loop_new (NULL, FALSE); + g_main_loop_run (loop); + + g_main_loop_unref (loop); + g_object_unref (handler); + g_object_unref (manager); + + return 0; +} diff --git a/examples/client/dbus-tubes/constants.h b/examples/client/dbus-tubes/constants.h new file mode 100644 index 000000000..77d9b1cec --- /dev/null +++ b/examples/client/dbus-tubes/constants.h @@ -0,0 +1,9 @@ +#ifndef DBUS_TUBE_EXAMPLE_CONSTANTS_H +#define DBUS_TUBE_EXAMPLE_CONSTANTS_H + +#define EXAMPLE_SERVICE_NAME "uk.co.example.calculator" + +#define EXAMPLE_INTERFACE "org.example.terriblecalculator" +#define EXAMPLE_PATH "/org/example/calculator" + +#endif diff --git a/examples/client/dbus-tubes/offerer.c b/examples/client/dbus-tubes/offerer.c new file mode 100644 index 000000000..87ffbb9e0 --- /dev/null +++ b/examples/client/dbus-tubes/offerer.c @@ -0,0 +1,238 @@ +#include <telepathy-glib/telepathy-glib.h> +#include "constants.h" + +static GMainLoop *loop = NULL; + +static void +connection_closed_cb ( + GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + GDBusConnection *connection = G_DBUS_CONNECTION (source); + GError *error = NULL; + + if (!g_dbus_connection_close_finish (connection, result, &error)) + { + g_warning ("Couldn't close connection: %s", error->message); + g_clear_error (&error); + } + else + { + g_debug ("Connection closed."); + } + + tp_channel_close_async (TP_CHANNEL (user_data), NULL, NULL); + g_object_unref (connection); +} + +static void +handle_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) +{ + if (tp_strdiff (method_name, "Add")) + { + g_dbus_method_invocation_return_error (invocation, + G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD, + "Unknown method '%s' on interface " EXAMPLE_INTERFACE, + method_name); + } + else if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(ii)"))) + { + g_dbus_method_invocation_return_error (invocation, + G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, + "Add takes two int32 parameters, not %s", + g_variant_get_type_string (parameters)); + } + else /* hooray! */ + { + guint x, y; + gboolean ret; + + g_variant_get (parameters, "(ii)", &x, &y); + + g_dbus_method_invocation_return_value (invocation, + g_variant_new ("(i)", x + y)); + + ret = g_dbus_connection_emit_signal (connection, + NULL, object_path, interface_name, "LuckyNumber", + g_variant_new ("(u)", g_random_int ()), + NULL); + /* "This can only fail if 'parameters' is not compatible with the D-Bus + * protocol." + */ + g_return_if_fail (ret); + + g_dbus_connection_flush_sync (connection, NULL, NULL); + g_dbus_connection_close (connection, NULL, connection_closed_cb, user_data); + } +} + +static void +register_object (GDBusConnection *connection, + TpDBusTubeChannel *channel) +{ + GDBusNodeInfo *introspection_data; + guint registration_id; + static const GDBusInterfaceVTable interface_vtable = + { + handle_method_call, + NULL, + NULL, + }; + static const gchar introspection_xml[] = + "<node>" + " <interface name='" EXAMPLE_INTERFACE "'>" + " <method name='Add'>" + " <arg type='i' name='x' direction='in'/>" + " <arg type='i' name='y' direction='in'/>" + " <arg type='i' name='result' direction='out'/>" + " </method>" + " <signal name='LuckyNumber'>" + " <arg type='u' name='number'/>" + " </signal>" + " </interface>" + "</node>"; + + introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL); + g_assert (introspection_data != NULL); + + registration_id = g_dbus_connection_register_object (connection, + EXAMPLE_PATH, introspection_data->interfaces[0], + &interface_vtable, g_object_ref (channel), g_object_unref, NULL); + g_assert (registration_id > 0); + + g_dbus_node_info_unref (introspection_data); +} + +static void +tube_offered (GObject *tube, + GAsyncResult *res, + gpointer user_data) +{ + GError *error = NULL; + GDBusConnection *conn; + + conn = tp_dbus_tube_channel_offer_finish (TP_DBUS_TUBE_CHANNEL (tube), res, + &error); + if (conn == NULL) + { + g_debug ("Failed to offer tube: %s", error->message); + g_error_free (error); + tp_channel_close_async (TP_CHANNEL (tube), NULL, NULL); + return; + } + + g_debug ("Tube opened"); + register_object (conn, TP_DBUS_TUBE_CHANNEL (tube)); +} + +static void +tube_invalidated_cb (TpStreamTubeChannel *tube, + guint domain, + gint code, + gchar *message, + gpointer user_data) +{ + g_debug ("Tube has been invalidated: %s", message); + g_main_loop_quit (loop); + g_object_unref (tube); +} + +static void +channel_created (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + TpChannel *channel; + GError *error = NULL; + TpDBusTubeChannel *tube; + + channel = tp_account_channel_request_create_and_handle_channel_finish ( + TP_ACCOUNT_CHANNEL_REQUEST (source), result, NULL, &error); + if (channel == NULL) + { + g_debug ("Failed to create channel: %s", error->message); + g_error_free (error); + g_main_loop_quit (loop); + return; + } + + g_debug ("Channel created: %s", tp_proxy_get_object_path (channel)); + + tube = TP_DBUS_TUBE_CHANNEL (channel); + + g_signal_connect (tube, "invalidated", + G_CALLBACK (tube_invalidated_cb), NULL); + + tp_dbus_tube_channel_offer_async (tube, NULL, tube_offered, NULL); +} + +int +main (int argc, + const char **argv) +{ + TpDBusDaemon *dbus; + TpAccount *account; + char *account_path; + GError *error = NULL; + TpAccountChannelRequest *req; + GHashTable *request; + + g_type_init (); + + if (argc != 3) + g_error ("Usage: offerer gabble/jabber/ladygaga t-pain@example.com"); + + dbus = tp_dbus_daemon_dup (&error); + g_assert_no_error (error); + + account_path = g_strconcat (TP_ACCOUNT_OBJECT_PATH_BASE, argv[1], NULL); + account = tp_account_new (dbus, account_path, &error); + g_assert_no_error (error); + g_free (account_path); + + request = tp_asv_new ( + TP_PROP_CHANNEL_CHANNEL_TYPE, + G_TYPE_STRING, + TP_IFACE_CHANNEL_TYPE_DBUS_TUBE, + + TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, + G_TYPE_UINT, + TP_HANDLE_TYPE_CONTACT, + + TP_PROP_CHANNEL_TARGET_ID, + G_TYPE_STRING, + argv[2], + + TP_PROP_CHANNEL_TYPE_DBUS_TUBE_SERVICE_NAME, + G_TYPE_STRING, + EXAMPLE_SERVICE_NAME, + + NULL); + + g_debug ("Offer channel to %s", argv[2]); + + req = tp_account_channel_request_new (account, request, + TP_USER_ACTION_TIME_CURRENT_TIME); + + tp_account_channel_request_create_and_handle_channel_async (req, NULL, + channel_created, NULL); + + loop = g_main_loop_new (NULL, FALSE); + g_main_loop_run (loop); + + g_object_unref (account); + g_object_unref (req); + g_hash_table_unref (request); + g_main_loop_unref (loop); + + return 0; +} diff --git a/examples/client/media-observer.c b/examples/client/media-observer.c index a65b32b22..07065f3d4 100644 --- a/examples/client/media-observer.c +++ b/examples/client/media-observer.c @@ -24,12 +24,8 @@ chan_invalidated_cb (TpProxy *proxy, gpointer user_data) { TpChannel *channel = TP_CHANNEL (proxy); - GHashTable *props; - props = tp_channel_borrow_immutable_properties (channel); - - g_print ("Call with %s terminated\n", - tp_asv_get_string (props, TP_PROP_CHANNEL_TARGET_ID)); + g_print ("Call with %s terminated\n", tp_channel_get_identifier (channel)); g_object_unref (channel); } @@ -52,7 +48,6 @@ observe_channels_cb (TpSimpleObserver *self, for (l = channels; l != NULL; l = g_list_next (l)) { TpChannel *channel = l->data; - GHashTable *props; gboolean requested; /* @@ -61,14 +56,13 @@ observe_channels_cb (TpSimpleObserver *self, continue; */ - props = tp_channel_borrow_immutable_properties (channel); - requested = tp_asv_get_boolean (props, TP_PROP_CHANNEL_REQUESTED, NULL); + requested = tp_channel_get_requested (channel); g_print ("Observing %s %s call %s %s\n", recovering? "existing": "new", requested? "outgoing": "incoming", requested? "to": "from", - tp_asv_get_string (props, TP_PROP_CHANNEL_TARGET_ID)); + tp_channel_get_identifier (channel)); g_signal_connect (g_object_ref (channel), "invalidated", G_CALLBACK (chan_invalidated_cb), NULL); diff --git a/examples/client/python/file-transfer.py b/examples/client/python/file-transfer.py index e47bc4575..cccfae857 100755 --- a/examples/client/python/file-transfer.py +++ b/examples/client/python/file-transfer.py @@ -31,9 +31,13 @@ def state_changed_cb(channel, pspec, data): channel.provide_file_async(file, provide_file_cb, None) def create_channel_cb(request, result, data): - (chan, context) = request.create_and_handle_channel_finish(result) + try: + (chan, context) = request.create_and_handle_channel_finish(result) - chan.connect('notify::state', state_changed_cb, data) + chan.connect('notify::state', state_changed_cb, data) + except GObject.GError, e: + print "Failed to create channel: %s" % e + sys.exit(1) if __name__ == '__main__': #TelepathyGLib.debug_set_flags("all") diff --git a/examples/client/stream-tubes/Makefile.am b/examples/client/stream-tubes/Makefile.am index dd648edb5..5cc7340f6 100644 --- a/examples/client/stream-tubes/Makefile.am +++ b/examples/client/stream-tubes/Makefile.am @@ -9,11 +9,11 @@ accepter_SOURCES = accepter.c # In an external project you'd use $(TP_GLIB_LIBS) (obtained from # pkg-config via autoconf) instead of these .la paths LDADD = \ - @DBUS_LIBS@ \ - @GLIB_LIBS@ \ $(top_builddir)/telepathy-glib/libtelepathy-glib-1.la \ $(top_builddir)/telepathy-glib/libtelepathy-glib-1-dbus.la \ $(top_builddir)/telepathy-glib/libtelepathy-glib-1-core.la \ + @DBUS_LIBS@ \ + @GLIB_LIBS@ \ $(NULL) AM_CFLAGS = \ diff --git a/telepathy-glib/Makefile.am b/telepathy-glib/Makefile.am index 710fbe159..3b5b76d8c 100644 --- a/telepathy-glib/Makefile.am +++ b/telepathy-glib/Makefile.am @@ -256,6 +256,7 @@ libtelepathy_glib_main_internal_la_SOURCES = \ contact-search.c \ contact-search-internal.h \ contact-search-result.c \ + base-contact-list-internal.h \ base-contact-list.c \ cm-message.c \ cm-message-internal.h \ @@ -338,7 +339,7 @@ include abi.am include codegen.am include introspection.am -Android.mk: Makefile.am +Android.mk: Makefile.am $(codegen_sources) androgenizer -:PROJECT telepathy-glib \ \ -:SHARED libtelepathy-glib-1-core -:TAGS eng debug \ diff --git a/telepathy-glib/account.c b/telepathy-glib/account.c index 56d5dd3b6..18d773bca 100644 --- a/telepathy-glib/account.c +++ b/telepathy-glib/account.c @@ -2373,7 +2373,7 @@ tp_account_get_parameters (TpAccount *account) * Returns: (transfer full): the dictionary of * parameters on @account, of type %G_VARIANT_TYPE_VARDICT * - * Since: 0.UNRELEASED + * Since: 0.17.6 */ GVariant * tp_account_dup_parameters_vardict (TpAccount *account) @@ -2770,7 +2770,7 @@ tp_account_update_parameters_finish (TpAccount *account, * you can pass the result of g_variant_new() or g_variant_new_parsed() * directly to this function without additional reference-count management. * - * Since: 0.UNRELEASED + * Since: 0.17.6 */ void tp_account_update_parameters_vardict_async (TpAccount *account, @@ -2807,7 +2807,7 @@ tp_account_update_parameters_vardict_async (TpAccount *account, * * Returns: %TRUE if the request succeeded, otherwise %FALSE * - * Since: 0.UNRELEASED + * Since: 0.17.6 */ gboolean tp_account_update_parameters_vardict_finish (TpAccount *account, @@ -3729,7 +3729,7 @@ tp_account_get_detailed_error (TpAccount *self, * * Returns: (transfer full) (allow-none): a D-Bus error name, or %NULL. * - * Since: 0.UNRELEASED + * Since: 0.17.6 */ gchar * tp_account_dup_detailed_error_vardict (TpAccount *self, @@ -3899,7 +3899,7 @@ tp_account_get_storage_specific_information_async (TpAccount *self, * call tp_account_dup_storage_specific_information_vardict_finish() to get the * result of the request. * - * Since: 0.UNRELEASED + * Since: 0.17.6 */ void tp_account_dup_storage_specific_information_vardict_async (TpAccount *self, diff --git a/telepathy-glib/automatic-client-factory.c b/telepathy-glib/automatic-client-factory.c index b34f3f60a..a63a82a38 100644 --- a/telepathy-glib/automatic-client-factory.c +++ b/telepathy-glib/automatic-client-factory.c @@ -81,6 +81,10 @@ * <para>%TP_CALL_CHANNEL_FEATURE_CORE * for #TpCallChannel</para> * </listitem> + * <listitem> + * <para>%TP_DBUS_TUBE_CHANNEL_FEATURE_CORE + * for #TpDBusTubeChannel</para> + * </listitem> * </itemizedlist> * * Since: 0.15.5 @@ -120,6 +124,75 @@ G_DEFINE_TYPE (TpAutomaticClientFactory, tp_automatic_client_factory, #define chainup ((TpSimpleClientFactoryClass *) \ tp_automatic_client_factory_parent_class) +typedef gboolean (*CheckPropertiesFunc) ( + const gchar *object_path, + const GHashTable *properties); + +typedef TpChannel *(*NewFunc) ( + TpSimpleClientFactory *client, + TpConnection *conn, + const gchar *object_path, + const GHashTable *properties, + GError **error); + +typedef struct { + const gchar *channel_type; + GType gtype; + CheckPropertiesFunc check_properties; + NewFunc new_func; + /* 0-terminated. All of a sudden, 3 is not such a scary number. */ + GQuark features[3]; +} ChannelTypeMapping; + +static ChannelTypeMapping *channel_type_mapping = NULL; + +static void +build_channel_type_mapping (void) +{ + ChannelTypeMapping i_hate_c[] = { + { TP_IFACE_CHANNEL_TYPE_STREAM_TUBE, + TP_TYPE_STREAM_TUBE_CHANNEL, + NULL, + (NewFunc) _tp_stream_tube_channel_new_with_factory, + { 0 }, + }, + { TP_IFACE_CHANNEL_TYPE_DBUS_TUBE, + TP_TYPE_DBUS_TUBE_CHANNEL, + NULL, + (NewFunc) _tp_dbus_tube_channel_new_with_factory, + { TP_DBUS_TUBE_CHANNEL_FEATURE_CORE, + 0 }, + }, + { TP_IFACE_CHANNEL_TYPE_TEXT, + TP_TYPE_TEXT_CHANNEL, + NULL, + (NewFunc) _tp_text_channel_new_with_factory, + { TP_TEXT_CHANNEL_FEATURE_INCOMING_MESSAGES, + TP_TEXT_CHANNEL_FEATURE_SMS, + 0 }, + }, + { TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, + TP_TYPE_FILE_TRANSFER_CHANNEL, + NULL, + (NewFunc) _tp_file_transfer_channel_new_with_factory, + { TP_FILE_TRANSFER_CHANNEL_FEATURE_CORE, + 0 }, + }, + { TP_IFACE_CHANNEL_TYPE_CALL, + TP_TYPE_CALL_CHANNEL, + NULL, + (NewFunc) _tp_call_channel_new_with_factory, + { TP_CALL_CHANNEL_FEATURE_CORE, + 0 }, + }, + { NULL } + }; + + g_return_if_fail (channel_type_mapping == NULL); + + channel_type_mapping = g_memdup (i_hate_c, sizeof i_hate_c); +} + static TpChannel * create_channel_impl (TpSimpleClientFactory *self, TpConnection *conn, @@ -128,33 +201,20 @@ create_channel_impl (TpSimpleClientFactory *self, GError **error) { const gchar *chan_type; + ChannelTypeMapping *m; chan_type = tp_asv_get_string (properties, TP_PROP_CHANNEL_CHANNEL_TYPE); - if (!tp_strdiff (chan_type, TP_IFACE_CHANNEL_TYPE_STREAM_TUBE)) + for (m = channel_type_mapping; m->channel_type != NULL; m++) { - return (TpChannel *) _tp_stream_tube_channel_new_with_factory (self, conn, - object_path, properties, error); - } - else if (!tp_strdiff (chan_type, TP_IFACE_CHANNEL_TYPE_DBUS_TUBE)) - { - return (TpChannel *) _tp_dbus_tube_channel_new_with_factory (self, conn, - object_path, properties, error); - } - else if (!tp_strdiff (chan_type, TP_IFACE_CHANNEL_TYPE_TEXT)) - { - return (TpChannel *) _tp_text_channel_new_with_factory (self, conn, - object_path, properties, error); - } - else if (!tp_strdiff (chan_type, TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER)) - { - return (TpChannel *) _tp_file_transfer_channel_new_with_factory (self, - conn, object_path, properties, error); - } - else if (!tp_strdiff (chan_type, TP_IFACE_CHANNEL_TYPE_CALL)) - { - return (TpChannel *) _tp_call_channel_new_with_factory (self, - conn, object_path, properties, error); + if (tp_strdiff (chan_type, m->channel_type)) + continue; + + if (m->check_properties != NULL && + !m->check_properties (object_path, properties)) + break; + + return m->new_func (self, conn, object_path, properties, error); } /* Chainup on parent implementation as fallback */ @@ -166,34 +226,26 @@ dup_channel_features_impl (TpSimpleClientFactory *self, TpChannel *channel) { GArray *features; - GQuark feature; + GQuark standard_features[] = { + TP_CHANNEL_FEATURE_GROUP, + TP_CHANNEL_FEATURE_PASSWORD, + }; + ChannelTypeMapping *m; /* Chainup to get desired features for all channel types */ features = chainup->dup_channel_features (self, channel); - feature = TP_CHANNEL_FEATURE_GROUP; - g_array_append_val (features, feature); - - feature = TP_CHANNEL_FEATURE_PASSWORD; - g_array_append_val (features, feature); - - if (TP_IS_TEXT_CHANNEL (channel)) - { - feature = TP_TEXT_CHANNEL_FEATURE_INCOMING_MESSAGES; - g_array_append_val (features, feature); + g_array_append_vals (features, standard_features, G_N_ELEMENTS (standard_features)); - feature = TP_TEXT_CHANNEL_FEATURE_SMS; - g_array_append_val (features, feature); - } - else if (TP_IS_FILE_TRANSFER_CHANNEL (channel)) - { - feature = TP_FILE_TRANSFER_CHANNEL_FEATURE_CORE; - g_array_append_val (features, feature); - } - else if (TP_IS_CALL_CHANNEL (channel)) + for (m = channel_type_mapping; m->channel_type != NULL; m++) { - feature = TP_CALL_CHANNEL_FEATURE_CORE; - g_array_append_val (features, feature); + if (G_TYPE_CHECK_INSTANCE_TYPE (channel, m->gtype)) + { + guint j; + for (j = 0; m->features[j] != 0; j++) + g_array_append_val (features, m->features[j]); + break; + } } return features; @@ -211,6 +263,8 @@ tp_automatic_client_factory_class_init (TpAutomaticClientFactoryClass *cls) simple_class->create_channel = create_channel_impl; simple_class->dup_channel_features = dup_channel_features_impl; + + build_channel_type_mapping (); } /** diff --git a/telepathy-glib/base-call-internal.h b/telepathy-glib/base-call-internal.h index 9e405477f..3fbbeace7 100644 --- a/telepathy-glib/base-call-internal.h +++ b/telepathy-glib/base-call-internal.h @@ -86,10 +86,10 @@ const gchar *_tp_base_call_channel_get_initial_tones (TpBaseCallChannel *self); void _tp_base_media_call_channel_endpoint_state_changed ( TpBaseMediaCallChannel *self); gboolean _tp_base_media_channel_is_held (TpBaseMediaCallChannel *self); -void _tp_base_media_call_channel_streams_sending_state_changed ( +gboolean _tp_base_media_call_channel_streams_sending_state_changed ( TpBaseMediaCallChannel *self, gboolean success); -void _tp_base_media_call_channel_streams_receiving_state_changed ( +gboolean _tp_base_media_call_channel_streams_receiving_state_changed ( TpBaseMediaCallChannel *self, gboolean success); diff --git a/telepathy-glib/base-call-stream.c b/telepathy-glib/base-call-stream.c index 16c2b7217..326a4a0d8 100644 --- a/telepathy-glib/base-call-stream.c +++ b/telepathy-glib/base-call-stream.c @@ -125,6 +125,8 @@ enum { PROP_OBJECT_PATH = 1, PROP_CONNECTION, + PROP_CONTENT, + PROP_CHANNEL, /* Call interface properties */ PROP_INTERFACES, @@ -225,6 +227,12 @@ tp_base_call_stream_get_property ( case PROP_OBJECT_PATH: g_value_set_string (value, self->priv->object_path); break; + case PROP_CONTENT: + g_value_set_object (value, self->priv->content); + break; + case PROP_CHANNEL: + g_value_set_object (value, self->priv->channel); + break; case PROP_REMOTE_MEMBERS: g_value_set_boxed (value, self->priv->remote_members); break; @@ -277,6 +285,13 @@ tp_base_call_stream_set_property ( self->priv->conn = g_value_dup_object (value); g_assert (self->priv->conn != NULL); break; + case PROP_CONTENT: + { + TpBaseCallContent *content = g_value_get_object (value); + if (content) + _tp_base_call_stream_set_content (self, content); + } + break; case PROP_OBJECT_PATH: g_free (self->priv->object_path); self->priv->object_path = g_value_dup_string (value); @@ -349,6 +364,32 @@ tp_base_call_stream_class_init (TpBaseCallStreamClass *klass) g_object_class_install_property (object_class, PROP_OBJECT_PATH, param_spec); /** + * TpBaseCallStream:content: + * + * #TpBaseCallContent object that owns this call stream. + * + * Since: 0.17.6 + */ + param_spec = g_param_spec_object ("content", "TpBaseCallContent object", + "Tp Content object that owns this call stream", + TP_TYPE_BASE_CALL_CONTENT, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_CONTENT, param_spec); + + /** + * TpBaseCallStream:channel: + * + * #TpBaseChannel object that owns this call stream. + * + * Since: 0.17.5 + */ + param_spec = g_param_spec_object ("channel", "TpBaseCallChannel object", + "Tp base call channel object that owns this call stream", + TP_TYPE_BASE_CALL_CHANNEL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_CHANNEL, param_spec); + + /** * TpBaseCallStream:interfaces: * * Additional interfaces implemented by this stream. @@ -592,7 +633,8 @@ tp_base_call_stream_update_remote_sending_state (TpBaseCallStream *self, const gchar *dbus_reason, const gchar *message) { - gpointer state_p; + gpointer old_state_p = NULL; + TpSendingState old_state; gboolean exists; GHashTable *updates; GHashTable *identifiers; @@ -608,13 +650,14 @@ tp_base_call_stream_update_remote_sending_state (TpBaseCallStream *self, new_state = TP_SENDING_STATE_PENDING_SEND; exists = g_hash_table_lookup_extended (self->priv->remote_members, - GUINT_TO_POINTER (contact), NULL, &state_p); + GUINT_TO_POINTER (contact), NULL, &old_state_p); + old_state = GPOINTER_TO_UINT (old_state_p); - if (exists && GPOINTER_TO_UINT (state_p) == new_state) + if (exists && old_state == new_state) return FALSE; DEBUG ("Updating remote member %d state: %d => %d for stream %s", - contact, GPOINTER_TO_UINT (state_p), new_state, self->priv->object_path); + contact, old_state, new_state, self->priv->object_path); g_hash_table_insert (self->priv->remote_members, GUINT_TO_POINTER (contact), @@ -807,10 +850,14 @@ _tp_base_call_stream_set_content (TpBaseCallStream *self, { g_return_if_fail (TP_IS_BASE_CALL_STREAM (self)); g_return_if_fail (TP_IS_BASE_CALL_CONTENT (content)); - g_return_if_fail (self->priv->content == NULL); + g_return_if_fail (self->priv->content == NULL || + self->priv->content == content); self->priv->content = content; self->priv->channel = _tp_base_call_content_get_channel (content); + + g_object_notify (G_OBJECT (self), "content"); + g_object_notify (G_OBJECT (self), "channel"); } TpBaseCallContent * diff --git a/telepathy-glib/base-contact-list-internal.h b/telepathy-glib/base-contact-list-internal.h new file mode 100644 index 000000000..f97cf471c --- /dev/null +++ b/telepathy-glib/base-contact-list-internal.h @@ -0,0 +1,31 @@ +/* ContactList channel manager - internals + * + * Copyright © 2010 Collabora Ltd. + * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __TP_BASE_CONTACT_LIST_INTERNAL_H__ +#define __TP_BASE_CONTACT_LIST_INTERNAL_H__ + +#include <telepathy-glib/base-contact-list.h> + +G_BEGIN_DECLS + +char _tp_base_contact_list_presence_state_to_letter (TpSubscriptionState ps); + +G_END_DECLS + +#endif diff --git a/telepathy-glib/base-contact-list.c b/telepathy-glib/base-contact-list.c index 034b3be58..e9e064951 100644 --- a/telepathy-glib/base-contact-list.c +++ b/telepathy-glib/base-contact-list.c @@ -19,6 +19,7 @@ #include <config.h> #include <telepathy-glib/base-contact-list.h> +#include <telepathy-glib/base-contact-list-internal.h> #include <dbus/dbus-glib-lowlevel.h> @@ -138,6 +139,12 @@ * tp_base_contact_list_get_contact_list_persists(); if a subclass does not * implement this itself, the default implementation always returns %TRUE, * which is correct for most protocols + * @download_async: the implementation of + * tp_base_contact_list_download_async(); if a subclass does not implement + * this itself, the default implementation will raise + * TP_ERROR_NOT_IMPLEMENTED asynchronously. Since: 0.18.0 + * @download_finish: the implementation of + * tp_base_contact_list_download_finish(). Since: 0.18.0 * * The class of a #TpBaseContactList. * @@ -189,6 +196,17 @@ */ /** + * TpBaseContactListAsyncFunc: + * @self: the contact list manager + * @callback: a callback to call on success, failure or disconnection + * @user_data: user data for the callback + * + * Signature of a virtual method that needs no additional information. + * + * Since: 0.18.0 + */ + +/** * TpBaseContactListActOnContactsFunc: * @self: the contact list manager * @contacts: the contacts on which to act @@ -274,6 +292,10 @@ struct _TpBaseContactListPrivate gboolean svc_contact_list; gboolean svc_contact_groups; gboolean svc_contact_blocking; + + /* TRUE if the contact list must be downloaded at connection. Default is + * TRUE. */ + gboolean download_at_connection; }; struct _TpBaseContactListClassPrivate @@ -484,6 +506,7 @@ G_DEFINE_INTERFACE (TpMutableContactGroupList, tp_mutable_contact_group_list, enum { PROP_CONNECTION = 1, + PROP_DOWNLOAD_AT_CONNECTION, N_PROPS }; @@ -567,6 +590,10 @@ tp_base_contact_list_get_property (GObject *object, g_value_set_object (value, self->priv->conn); break; + case PROP_DOWNLOAD_AT_CONNECTION: + g_value_set_boolean (value, self->priv->download_at_connection); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; @@ -589,6 +616,10 @@ tp_base_contact_list_set_property (GObject *object, g_object_set_qdata ((GObject *) self->priv->conn, BASE_CONTACT_LIST, self); break; + case PROP_DOWNLOAD_AT_CONNECTION: + self->priv->download_at_connection = g_value_get_boolean (value); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; @@ -621,6 +652,7 @@ tp_base_contact_list_constructed (GObject *object) g_return_if_fail (cls->dup_contacts != NULL); g_return_if_fail (cls->dup_states != NULL); g_return_if_fail (cls->get_contact_list_persists != NULL); + g_return_if_fail (cls->download_async != NULL); self->priv->svc_contact_list = TP_IS_SVC_CONNECTION_INTERFACE_CONTACT_LIST (self->priv->conn); @@ -714,6 +746,16 @@ tp_base_contact_list_simple_finish (TpBaseContactList *self, } static void +tp_base_contact_list_download_async_default (TpBaseContactList *self, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_simple_async_report_error_in_idle (G_OBJECT (self), callback, + user_data, TP_ERRORS, TP_ERROR_NOT_IMPLEMENTED, + "This CM does not implement Download"); +} + +static void tp_mutable_contact_list_default_init (TpMutableContactListInterface *iface) { iface->request_subscription_finish = tp_base_contact_list_simple_finish; @@ -774,6 +816,8 @@ tp_base_contact_list_class_init (TpBaseContactListClass *cls) /* defaults */ cls->get_contact_list_persists = tp_base_contact_list_true_func; + cls->download_async = tp_base_contact_list_download_async_default; + cls->download_finish = tp_base_contact_list_simple_finish; object_class->get_property = tp_base_contact_list_get_property; object_class->set_property = tp_base_contact_list_set_property; @@ -793,6 +837,23 @@ tp_base_contact_list_class_init (TpBaseContactListClass *cls) "The connection that owns this channel manager", TP_TYPE_BASE_CONNECTION, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + + /** + * TpBaseContactList:download-at-connection: + * + * Whether the roster should be automatically downloaded at connection. + * + * This property doesn't change anything in TpBaseContactsList's behaviour. + * Implementations should check this property when they become connected + * and in their Download method, and behave accordingly. + * + * Since: 0.18.0 + */ + g_object_class_install_property (object_class, PROP_DOWNLOAD_AT_CONNECTION, + g_param_spec_boolean ("download-at-connection", "Download at connection", + "Whether the roster should be automatically downloaded at connection", + TRUE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); } /** @@ -976,8 +1037,8 @@ tp_base_contact_list_set_list_received (TpBaseContactList *self) self->priv->conn, self->priv->state); } -static char -presence_state_to_letter (TpSubscriptionState ps) +char +_tp_base_contact_list_presence_state_to_letter (TpSubscriptionState ps) { switch (ps) { @@ -1068,8 +1129,9 @@ tp_base_contact_list_contacts_changed_internal (TpBaseContactList *self, DEBUG ("Contact %s: subscribe=%c publish=%c '%s'", tp_handle_inspect (self->priv->contact_repo, contact), - presence_state_to_letter (subscribe), - presence_state_to_letter (publish), publish_request); + _tp_base_contact_list_presence_state_to_letter (subscribe), + _tp_base_contact_list_presence_state_to_letter (publish), + publish_request); g_hash_table_insert (changes, GUINT_TO_POINTER (contact), tp_value_array_build (3, @@ -1907,6 +1969,82 @@ tp_base_contact_list_get_contact_list_persists (TpBaseContactList *self) } /** + * tp_base_contact_list_get_download_at_connection: + * @self: a contact list manager + * + * This function returns the + * #TpBaseContactList:download-at-connection property. + * + * Returns: the #TpBaseContactList:download-at-connection property + * + * Since: 0.18.0 + */ +gboolean +tp_base_contact_list_get_download_at_connection (TpBaseContactList *self) +{ + return self->priv->download_at_connection; +} + +/** + * tp_base_contact_list_download_async: + * @self: a contact list manager + * @callback: a callback to call when the operation succeeds or fails + * @user_data: optional data to pass to @callback + * + * Download the contact list when it is not done automatically at + * connection. + * + * If the #TpBaseContactList subclass does not override + * download_async, the default implementation will raise + * TP_ERROR_NOT_IMPLEMENTED asynchronously. + * + * Since: 0.18.0 + */ +void +tp_base_contact_list_download_async (TpBaseContactList *self, + GAsyncReadyCallback callback, + gpointer user_data) +{ + TpBaseContactListClass *cls = TP_BASE_CONTACT_LIST_GET_CLASS (self); + + g_return_if_fail (cls != NULL); + g_return_if_fail (cls->download_async != NULL); + + return cls->download_async (self, callback, user_data); +} + +/** + * tp_base_contact_list_download_finish: + * @self: a contact list manager + * @result: the result passed to @callback by an implementation of + * tp_base_contact_list_download_async() + * @error: used to raise an error if %FALSE is returned + * + * Interpret the result of an asynchronous call to + * tp_base_contact_list_download_async(). + * + * This is a virtual method which may be implemented using + * #TpContactListClass.download_finish. If the @result + * will be a #GSimpleAsyncResult, the default implementation may be used. + * + * Returns: %TRUE on success or %FALSE on error + * + * Since: 0.18.0 + */ +gboolean +tp_base_contact_list_download_finish (TpBaseContactList *self, + GAsyncResult *result, + GError **error) +{ + TpBaseContactListClass *cls = TP_BASE_CONTACT_LIST_GET_CLASS (self); + + g_return_val_if_fail (cls != NULL, FALSE); + g_return_val_if_fail (cls->download_finish != NULL, FALSE); + + return cls->download_finish (self, result, error); +} + +/** * tp_base_contact_list_get_request_uses_message: * @self: a contact list manager * @@ -3926,6 +4064,7 @@ typedef enum { LP_CONTACT_LIST_PERSISTS, LP_CAN_CHANGE_CONTACT_LIST, LP_REQUEST_USES_MESSAGE, + LP_DOWNLOAD_AT_CONNECTION, NUM_LIST_PROPERTIES } ListProp; @@ -3934,6 +4073,7 @@ static TpDBusPropertiesMixinPropImpl known_list_props[] = { { "ContactListPersists", GINT_TO_POINTER (LP_CONTACT_LIST_PERSISTS), }, { "CanChangeContactList", GINT_TO_POINTER (LP_CAN_CHANGE_CONTACT_LIST) }, { "RequestUsesMessage", GINT_TO_POINTER (LP_REQUEST_USES_MESSAGE) }, + { "DownloadAtConnection", GINT_TO_POINTER (LP_DOWNLOAD_AT_CONNECTION) }, { NULL } }; @@ -3975,6 +4115,11 @@ tp_base_contact_list_get_list_dbus_property (GObject *conn, tp_base_contact_list_get_request_uses_message (self)); break; + case LP_DOWNLOAD_AT_CONNECTION: + g_return_if_fail (G_VALUE_HOLDS_BOOLEAN (value)); + g_value_set_boolean (value, self->priv->download_at_connection); + break; + default: g_return_if_reached (); } @@ -4029,6 +4174,31 @@ tp_base_contact_list_fill_list_contact_attributes (GObject *obj, } } +static void +tp_base_contact_list_mixin_download_cb (GObject *source, + GAsyncResult *result, + gpointer context) +{ + TpBaseContactList *self = TP_BASE_CONTACT_LIST (source); + GError *error = NULL; + + tp_base_contact_list_download_finish (self, result, &error); + tp_base_contact_list_mixin_return_void (context, error); + g_clear_error (&error); +} + +static void +tp_base_contact_list_mixin_download ( + TpSvcConnectionInterfaceContactList *svc, + DBusGMethodInvocation *context) +{ + TpBaseContactList *self = g_object_get_qdata ((GObject *) svc, + BASE_CONTACT_LIST); + + tp_base_contact_list_download_async (self, + tp_base_contact_list_mixin_download_cb, context); +} + /** * tp_base_contact_list_mixin_list_iface_init: * @klass: the service-side D-Bus interface @@ -4053,6 +4223,7 @@ tp_base_contact_list_mixin_list_iface_init ( IMPLEMENT (remove_contacts); IMPLEMENT (unsubscribe); IMPLEMENT (unpublish); + IMPLEMENT (download); #undef IMPLEMENT } diff --git a/telepathy-glib/base-contact-list.h b/telepathy-glib/base-contact-list.h index 84b1cd9a9..a23dcaf40 100644 --- a/telepathy-glib/base-contact-list.h +++ b/telepathy-glib/base-contact-list.h @@ -64,6 +64,8 @@ TpContactListState tp_base_contact_list_get_state (TpBaseContactList *self, GError **error); TpBaseConnection *tp_base_contact_list_get_connection ( TpBaseContactList *self, GError **error); +gboolean tp_base_contact_list_get_download_at_connection ( + TpBaseContactList *self); /* ---- Called by subclasses for ContactList (or both) ---- */ @@ -112,6 +114,23 @@ void tp_base_contact_list_dup_states (TpBaseContactList *self, TpSubscriptionState *publish, gchar **publish_request); +typedef void (*TpBaseContactListAsyncFunc) ( + TpBaseContactList *self, + GAsyncReadyCallback callback, + gpointer user_data); + +void tp_base_contact_list_download_async (TpBaseContactList *self, + GAsyncReadyCallback callback, + gpointer user_data); + +gboolean tp_base_contact_list_download_finish (TpBaseContactList *self, + GAsyncResult *result, + GError **error); + +typedef gboolean (*TpBaseContactListAsyncFinishFunc) (TpBaseContactList *self, + GAsyncResult *result, + GError **error); + struct _TpBaseContactListClass { GObjectClass parent_class; @@ -119,8 +138,11 @@ struct _TpBaseContactListClass { TpBaseContactListDupStatesFunc dup_states; TpBaseContactListBooleanFunc get_contact_list_persists; + TpBaseContactListAsyncFunc download_async; + TpBaseContactListAsyncFinishFunc download_finish; + /*<private>*/ - GCallback _padding[7]; + GCallback _padding[5]; TpBaseContactListClassPrivate *priv; }; @@ -152,10 +174,6 @@ typedef void (*TpBaseContactListActOnContactsFunc) ( GAsyncReadyCallback callback, gpointer user_data); -typedef gboolean (*TpBaseContactListAsyncFinishFunc) (TpBaseContactList *self, - GAsyncResult *result, - GError **error); - struct _TpMutableContactListInterface { GTypeInterface parent; diff --git a/telepathy-glib/base-media-call-channel.c b/telepathy-glib/base-media-call-channel.c index f6155ad9d..ab8bf3625 100644 --- a/telepathy-glib/base-media-call-channel.c +++ b/telepathy-glib/base-media-call-channel.c @@ -378,6 +378,26 @@ update_hold_state (TpBaseMediaCallChannel *self) l2 != NULL; l2 = l2->next) { TpBaseMediaCallStream *stream = TP_BASE_MEDIA_CALL_STREAM (l2->data); + GHashTable *members = _tp_base_call_stream_get_remote_members ( + TP_BASE_CALL_STREAM (stream)); + TpSendingState local = tp_base_call_stream_get_local_sending_state ( + TP_BASE_CALL_STREAM (stream)); + GHashTableIter iter; + gpointer key, value; + gboolean wants_receive = FALSE; + + g_hash_table_iter_init (&iter, members); + while (g_hash_table_iter_next (&iter, &key, &value)) + { + TpSendingState member_state = GPOINTER_TO_INT (value); + + if (member_state == TP_SENDING_STATE_PENDING_SEND || + member_state == TP_SENDING_STATE_SENDING) + { + wants_receive = TRUE; + break; + } + } tp_base_media_call_stream_update_receiving_state (stream); tp_base_media_call_stream_update_sending_state (stream); @@ -387,10 +407,12 @@ update_hold_state (TpBaseMediaCallChannel *self) tp_base_media_call_stream_get_receiving_state (stream) != TP_STREAM_FLOW_STATE_STOPPED) is_stopped = FALSE; - if (tp_base_media_call_stream_get_sending_state (stream) != - TP_STREAM_FLOW_STATE_STARTED || - tp_base_media_call_stream_get_receiving_state (stream) != - TP_STREAM_FLOW_STATE_STARTED) + if ((tp_base_media_call_stream_get_sending_state (stream) != + TP_STREAM_FLOW_STATE_STARTED && + local == TP_SENDING_STATE_SENDING) || + (tp_base_media_call_stream_get_receiving_state (stream) != + TP_STREAM_FLOW_STATE_STARTED && + wants_receive)) is_started = FALSE; } } @@ -414,11 +436,11 @@ hold_change_failed (TpBaseMediaCallChannel *self) TpBaseCallChannel *bcc = TP_BASE_CALL_CHANNEL (self); GList *l, *l2; - if (self->priv->hold_state == TP_LOCAL_HOLD_STATE_PENDING_UNHOLD) - { - set_hold_state (self, TP_LOCAL_HOLD_STATE_PENDING_HOLD, - TP_LOCAL_HOLD_STATE_REASON_RESOURCE_NOT_AVAILABLE); - } + if (self->priv->hold_state != TP_LOCAL_HOLD_STATE_PENDING_UNHOLD) + return; + + set_hold_state (self, TP_LOCAL_HOLD_STATE_PENDING_HOLD, + TP_LOCAL_HOLD_STATE_REASON_RESOURCE_NOT_AVAILABLE); for (l = tp_base_call_channel_get_contents (bcc); l != NULL; l = l->next) { @@ -552,24 +574,59 @@ _tp_base_media_channel_is_held (TpBaseMediaCallChannel *self) } } -void +gboolean _tp_base_media_call_channel_streams_sending_state_changed ( TpBaseMediaCallChannel *self, gboolean success) { + gboolean was_held = + (self->priv->hold_state != TP_LOCAL_HOLD_STATE_UNHELD); + if (success) update_hold_state (self); else hold_change_failed (self); + + return was_held; } -void +gboolean _tp_base_media_call_channel_streams_receiving_state_changed ( TpBaseMediaCallChannel *self, gboolean success) { + gboolean was_held = + (self->priv->hold_state != TP_LOCAL_HOLD_STATE_UNHELD); + if (success) update_hold_state (self); else hold_change_failed (self); + + return was_held; +} + +/** + * tp_base_media_call_channel_get_local_hold_state: + * @channel: a #TpBaseMediaCallChannel + * @reason: pointer to a location where to store the @reason, or %NULL + * + * <!-- --> + * + * Returns: The current hold state + * + * Since: 0.17.6 + */ + +TpLocalHoldState +tp_base_media_call_channel_get_local_hold_state ( + TpBaseMediaCallChannel *channel, TpLocalHoldStateReason *reason) +{ + g_return_val_if_fail (TP_IS_BASE_MEDIA_CALL_CHANNEL (channel), + TP_LOCAL_HOLD_STATE_UNHELD); + + if (reason) + *reason = channel->priv->hold_state_reason; + + return channel->priv->hold_state; } diff --git a/telepathy-glib/base-media-call-channel.h b/telepathy-glib/base-media-call-channel.h index 19e9caf9c..063a64d61 100644 --- a/telepathy-glib/base-media-call-channel.h +++ b/telepathy-glib/base-media-call-channel.h @@ -74,6 +74,9 @@ GType tp_base_media_call_channel_get_type (void); TP_TYPE_BASE_MEDIA_CALL_CHANNEL, TpBaseMediaCallChannelClass)) +TpLocalHoldState tp_base_media_call_channel_get_local_hold_state ( + TpBaseMediaCallChannel *channel, TpLocalHoldStateReason *reason); + G_END_DECLS #endif /* #ifndef __TP_BASE_MEDIA_CALL_CHANNEL_H__*/ diff --git a/telepathy-glib/base-media-call-content.c b/telepathy-glib/base-media-call-content.c index b0c9857e3..47fc6d2f8 100644 --- a/telepathy-glib/base-media-call-content.c +++ b/telepathy-glib/base-media-call-content.c @@ -871,6 +871,8 @@ _tp_base_media_call_content_ready_to_accept (TpBaseMediaCallContent *self) { tp_base_media_call_stream_set_local_sending (stream, TRUE); } + tp_base_media_call_stream_update_sending_state (stream); + g_hash_table_iter_init (&iter, members); while (g_hash_table_iter_next (&iter, &key, &value)) { @@ -910,6 +912,7 @@ _tp_base_media_call_content_remote_accepted (TpBaseMediaCallContent *self) if (local == TP_SENDING_STATE_SENDING) tp_base_media_call_stream_set_local_sending (stream, TRUE); + tp_base_media_call_stream_update_sending_state (stream); } } diff --git a/telepathy-glib/base-media-call-stream.c b/telepathy-glib/base-media-call-stream.c index b48c9a7f7..5df072777 100644 --- a/telepathy-glib/base-media-call-stream.c +++ b/telepathy-glib/base-media-call-stream.c @@ -145,6 +145,8 @@ #include "base-media-call-stream.h" +#include <string.h> + #define DEBUG_FLAG TP_DEBUG_CALL #include "telepathy-glib/base-call-content.h" #include "telepathy-glib/base-call-channel.h" @@ -211,6 +213,8 @@ struct _TpBaseMediaCallStreamPrivate gboolean local_sending; gboolean remotely_held; gboolean sending_stop_requested; + gboolean sending_failure; + gboolean receiving_failure; }; static GPtrArray *tp_base_media_call_stream_get_interfaces ( @@ -240,6 +244,10 @@ tp_base_media_call_stream_init (TpBaseMediaCallStream *self) g_signal_connect (self, "notify::remote-members", G_CALLBACK (tp_base_media_call_stream_update_receiving_state), NULL); + g_signal_connect (self, "notify::channel", + G_CALLBACK (tp_base_media_call_stream_update_receiving_state), NULL); + g_signal_connect (self, "notify::channel", + G_CALLBACK (tp_base_media_call_stream_update_sending_state), NULL); } static void @@ -619,7 +627,41 @@ tp_base_media_call_stream_set_stun_servers (TpBaseMediaCallStream *self, g_return_if_fail (TP_IS_BASE_MEDIA_CALL_STREAM (self)); g_return_if_fail (stun_servers != NULL); - tp_clear_pointer (&self->priv->stun_servers, g_ptr_array_unref); + if (self->priv->stun_servers != NULL) + { + if (stun_servers->len == self->priv->stun_servers->len) + { + guint i; + gboolean equal = TRUE; + + for (i = 0; i < stun_servers->len; i++) + { + GValueArray *gva1 = g_ptr_array_index (stun_servers, i); + GValueArray *gva2 = g_ptr_array_index (self->priv->stun_servers, + i); + gchar *ip1, *ip2; + guint port1, port2; + + tp_value_array_unpack (gva1, 2, &ip1, &port1); + tp_value_array_unpack (gva2, 2, &ip2, &port2); + + if (port1 != port2 || strcmp (ip1, ip2)) + { + equal = FALSE; + break; + } + } + + if (equal) + { + g_ptr_array_unref (stun_servers); + return; + } + } + + g_ptr_array_unref (self->priv->stun_servers); + } + self->priv->stun_servers = g_ptr_array_ref (stun_servers); tp_svc_call_stream_interface_media_emit_stun_servers_changed (self, @@ -840,9 +882,15 @@ tp_base_media_call_stream_update_sending_state (TpBaseMediaCallStream *self) goto done; } + if (!tp_base_call_channel_is_accepted (TP_BASE_CALL_CHANNEL (channel))) + goto done; + if (self->priv->remotely_held) goto done; + if (self->priv->sending_failure) + goto done; + sending = self->priv->local_sending; done: @@ -894,6 +942,23 @@ tp_base_media_call_stream_set_local_sending (TpBaseMediaCallStream *self, tp_base_media_call_stream_update_sending_state (self); } +/** + * tp_base_media_call_stream_get_local_sending: + * @self: a #TpBaseMediaCallStream + * + * Gets the local sending state + * + * Returns: The local sending state + * Since: 0.17.7 + */ +gboolean +tp_base_media_call_stream_get_local_sending (TpBaseMediaCallStream *self) +{ + g_return_val_if_fail (TP_IS_BASE_MEDIA_CALL_STREAM (self), FALSE); + + return self->priv->local_sending; +} + void _tp_base_media_call_stream_set_remotely_held (TpBaseMediaCallStream *self, gboolean remotely_held) @@ -947,6 +1012,9 @@ tp_base_media_call_stream_update_receiving_state (TpBaseMediaCallStream *self) if (channel == NULL || !_tp_base_call_channel_is_locally_accepted (channel)) goto done; + if (self->priv->receiving_failure) + goto done; + if (TP_IS_BASE_MEDIA_CALL_CHANNEL (channel)) { TpBaseMediaCallChannel *mediachan = TP_BASE_MEDIA_CALL_CHANNEL (channel); @@ -1183,26 +1251,35 @@ tp_base_media_call_stream_report_sending_failure ( TpStreamFlowState old_state = self->priv->sending_state; TpBaseCallChannel *channel = _tp_base_call_stream_get_channel ( TP_BASE_CALL_STREAM (self)); + gboolean was_held = FALSE; if (self->priv->sending_state == TP_STREAM_FLOW_STATE_STOPPED) goto done; + self->priv->sending_failure = TRUE; self->priv->sending_stop_requested = FALSE; - self->priv->sending_state = TP_STREAM_FLOW_STATE_STOPPED; - g_object_notify (G_OBJECT (self), "sending-state"); if (channel != NULL && TP_IS_BASE_MEDIA_CALL_CHANNEL (channel)) - _tp_base_media_call_channel_streams_sending_state_changed ( - TP_BASE_MEDIA_CALL_CHANNEL (channel), FALSE); + { + was_held = _tp_base_media_call_channel_streams_sending_state_changed ( + TP_BASE_MEDIA_CALL_CHANNEL (channel), FALSE); + } - if (klass->report_sending_failure != NULL) - klass->report_sending_failure (self, old_state, reason, dbus_reason, - message); + if (!was_held) + { + self->priv->local_sending = FALSE; + if (klass->report_sending_failure != NULL) + klass->report_sending_failure (self, old_state, reason, dbus_reason, + message); + } + g_object_notify (G_OBJECT (self), "sending-state"); tp_svc_call_stream_interface_media_emit_sending_state_changed (self, self->priv->sending_state); + self->priv->sending_failure = FALSE; + done: tp_svc_call_stream_interface_media_return_from_report_sending_failure ( context); @@ -1269,6 +1346,7 @@ tp_base_media_call_stream_report_receiving_failure ( TpStreamFlowState old_state = self->priv->receiving_state; TpBaseCallChannel *channel = _tp_base_call_stream_get_channel ( TP_BASE_CALL_STREAM (self)); + gboolean was_held = FALSE; /* Clear all receving requests, we can't receive */ tp_intset_clear (self->priv->receiving_requests); @@ -1277,19 +1355,23 @@ tp_base_media_call_stream_report_receiving_failure ( goto done; self->priv->receiving_state = TP_STREAM_FLOW_STATE_STOPPED; + self->priv->receiving_failure = TRUE; g_object_notify (G_OBJECT (self), "receiving-state"); if (channel != NULL && TP_IS_BASE_MEDIA_CALL_CHANNEL (channel)) - _tp_base_media_call_channel_streams_receiving_state_changed ( - TP_BASE_MEDIA_CALL_CHANNEL (channel), FALSE); + was_held = + _tp_base_media_call_channel_streams_receiving_state_changed ( + TP_BASE_MEDIA_CALL_CHANNEL (channel), FALSE); - if (klass->report_receiving_failure != NULL) + if (klass->report_receiving_failure != NULL && !was_held) klass->report_receiving_failure (self, old_state, reason, dbus_reason, message); tp_svc_call_stream_interface_media_emit_receiving_state_changed (self, self->priv->receiving_state); + self->priv->receiving_failure = FALSE; + done: tp_svc_call_stream_interface_media_return_from_report_receiving_failure ( context); diff --git a/telepathy-glib/base-media-call-stream.h b/telepathy-glib/base-media-call-stream.h index ec7e552f7..2db752ce4 100644 --- a/telepathy-glib/base-media-call-stream.h +++ b/telepathy-glib/base-media-call-stream.h @@ -118,6 +118,8 @@ void tp_base_media_call_stream_update_sending_state ( TpBaseMediaCallStream *self); void tp_base_media_call_stream_set_local_sending (TpBaseMediaCallStream *self, gboolean sending); +gboolean tp_base_media_call_stream_get_local_sending ( + TpBaseMediaCallStream *self); GPtrArray *tp_base_media_call_stream_get_local_candidates ( TpBaseMediaCallStream *self); diff --git a/telepathy-glib/call-channel.c b/telepathy-glib/call-channel.c index 121ec3c5c..092334132 100644 --- a/telepathy-glib/call-channel.c +++ b/telepathy-glib/call-channel.c @@ -86,10 +86,13 @@ struct _TpCallChannelPrivate gchar *initial_audio_name; gchar *initial_video_name; gboolean mutable_contents; + TpLocalHoldState hold_state; + TpLocalHoldStateReason hold_state_reason; GSimpleAsyncResult *core_result; gboolean properties_retrieved; gboolean initial_members_retrieved; + gboolean hold_state_retrieved; }; enum /* props */ @@ -105,6 +108,8 @@ enum /* props */ PROP_INITIAL_AUDIO_NAME, PROP_INITIAL_VIDEO_NAME, PROP_MUTABLE_CONTENTS, + PROP_HOLD_STATE, + PROP_HOLD_STATE_REASON, }; enum /* signals */ @@ -128,6 +133,7 @@ _tp_call_content_new (TpCallChannel *self, "dbus-connection", tp_proxy_get_dbus_connection (self), "object-path", object_path, "connection", tp_channel_borrow_connection ((TpChannel *) self), + "channel", self, NULL); } @@ -142,6 +148,8 @@ _tp_call_content_new (TpCallChannel *self, * the Telepathy namespace, a D-Bus error in any other namespace * (for implementation-specific errors), or the empty string to indicate that * the state change was not an error + * @message: A developer readable debug message giving the reason for the state + * change. * * Data structure representing the reason for a call state change. * @@ -151,7 +159,8 @@ _tp_call_content_new (TpCallChannel *self, static TpCallStateReason * _tp_call_state_reason_new_full (TpHandle actor, TpCallStateChangeReason reason, - const gchar *dbus_reason) + const gchar *dbus_reason, + const gchar *message) { TpCallStateReason *r; @@ -159,6 +168,7 @@ _tp_call_state_reason_new_full (TpHandle actor, r->actor = actor; r->reason = reason; r->dbus_reason = g_strdup (dbus_reason); + r->message = g_strdup (message); r->ref_count = 1; return r; @@ -170,13 +180,15 @@ _tp_call_state_reason_new (const GValueArray *value_array) TpHandle handle; TpCallStateChangeReason reason; const gchar *dbus_reason; + const gchar *message; - tp_value_array_unpack ((GValueArray *) value_array, 3, + tp_value_array_unpack ((GValueArray *) value_array, 4, &handle, &reason, - &dbus_reason); + &dbus_reason, + &message); - return _tp_call_state_reason_new_full (handle, reason, dbus_reason); + return _tp_call_state_reason_new_full (handle, reason, dbus_reason, message); } TpCallStateReason * @@ -194,6 +206,7 @@ _tp_call_state_reason_unref (TpCallStateReason *r) if (g_atomic_int_dec_and_test (&r->ref_count)) { g_free (r->dbus_reason); + g_free (r->message); g_slice_free (TpCallStateReason, r); } } @@ -350,6 +363,29 @@ content_removed_cb (TpChannel *channel, DEBUG ("Content '%s' removed but not found", object_path); } +static const gchar * +call_state_to_string (TpCallState state) +{ + switch (state) + { + case TP_CALL_STATE_UNKNOWN: + return "unknown"; + case TP_CALL_STATE_PENDING_INITIATOR: + return "pending-initiator"; + case TP_CALL_STATE_INITIALISING: + return "initialising"; + case TP_CALL_STATE_INITIALISED: + return "initialised"; + case TP_CALL_STATE_ACCEPTED: + return "accepted"; + case TP_CALL_STATE_ACTIVE: + return "active"; + case TP_CALL_STATE_ENDED: + return "ended"; + } + return "invalid"; +} + static void call_state_changed_cb (TpChannel *channel, guint state, @@ -364,7 +400,8 @@ call_state_changed_cb (TpChannel *channel, if (!self->priv->properties_retrieved) return; - DEBUG ("Call state changed to %u (flags: %u)", state, flags); + DEBUG ("Call state changed to %s (flags: %u)", call_state_to_string (state), + flags); tp_clear_pointer (&self->priv->state_reason, _tp_call_state_reason_unref); tp_clear_pointer (&self->priv->state_details, g_hash_table_unref); @@ -391,6 +428,22 @@ typedef struct TpCallStateReason *reason; } UpdateCallMembersData; + +static void +channel_maybe_core_prepared (TpCallChannel *self) +{ + if (self->priv->core_result == NULL) + return; + + if (self->priv->initial_members_retrieved && + self->priv->properties_retrieved && + self->priv->hold_state_retrieved) + { + g_simple_async_result_complete (self->priv->core_result); + g_clear_object (&self->priv->core_result); + } +} + static void update_call_members_prepared_cb (GObject *object, GAsyncResult *result, @@ -425,9 +478,7 @@ update_call_members_prepared_cb (GObject *object, { self->priv->initial_members_retrieved = TRUE; - g_assert (self->priv->core_result != NULL); - g_simple_async_result_complete (self->priv->core_result); - g_clear_object (&self->priv->core_result); + channel_maybe_core_prepared (self); } else { @@ -566,7 +617,47 @@ got_all_properties_cb (TpProxy *proxy, } /* core_result will be complete in update_call_members_prepared_cb() when - * the initial members are prepared. */ + * the initial members are prepared or when the hold state is retrived. */ +} + +static void +hold_state_changed_cb (TpChannel *proxy, + guint arg_HoldState, + guint arg_Reason, + gpointer user_data, GObject *weak_object) +{ + TpCallChannel *self = TP_CALL_CHANNEL (proxy); + + if (!self->priv->hold_state_retrieved) + return; + + self->priv->hold_state = arg_HoldState; + self->priv->hold_state_reason = arg_Reason; + + g_object_notify (G_OBJECT (proxy), "hold-state"); + g_object_notify (G_OBJECT (proxy), "hold-state-reason"); +} + +static void +got_hold_state_cb (TpChannel *proxy, guint arg_HoldState, guint arg_Reason, + const GError *error, gpointer user_data, GObject *weak_object) +{ + TpCallChannel *self = TP_CALL_CHANNEL (proxy); + + if (error != NULL) + { + DEBUG ("Could not get the call channel hold state: %s", error->message); + g_simple_async_result_set_from_error (self->priv->core_result, error); + g_simple_async_result_complete (self->priv->core_result); + g_clear_object (&self->priv->core_result); + return; + } + + self->priv->hold_state = arg_HoldState; + self->priv->hold_state_reason = arg_Reason; + self->priv->hold_state_retrieved = TRUE; + + channel_maybe_core_prepared (self); } static void @@ -594,6 +685,16 @@ _tp_call_channel_prepare_core_async (TpProxy *proxy, tp_cli_dbus_properties_call_get_all (self, -1, TP_IFACE_CHANNEL_TYPE_CALL, got_all_properties_cb, NULL, NULL, NULL); + + if (tp_proxy_has_interface_by_id (proxy, + TP_IFACE_QUARK_CHANNEL_INTERFACE_HOLD)) + { + tp_cli_channel_interface_hold_connect_to_hold_state_changed (channel, + hold_state_changed_cb, NULL, NULL, NULL, NULL); + + tp_cli_channel_interface_hold_call_get_hold_state (channel, -1, + got_hold_state_cb, NULL, NULL, NULL); + } } static void @@ -696,7 +797,15 @@ tp_call_channel_get_property (GObject *object, g_value_set_boolean (value, self->priv->mutable_contents); break; - default: + case PROP_HOLD_STATE: + g_value_set_uint (value, self->priv->hold_state); + break; + + case PROP_HOLD_STATE_REASON: + g_value_set_uint (value, self->priv->hold_state_reason); + break; + + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } @@ -795,7 +904,8 @@ tp_call_channel_class_init (TpCallChannelClass *klass) /** * TpCallChannel:state-details: * - * Detailed information about #TpCallChannel:state. + * Detailed infoermation about #TpCallChannel:state. It is a #GHashTable + * mapping gchar*->GValue, it can be accessed using the tp_asv_* functions. * * Since: 0.17.5 */ @@ -906,6 +1016,36 @@ tp_call_channel_class_init (TpCallChannelClass *klass) g_object_class_install_property (gobject_class, PROP_MUTABLE_CONTENTS, param_spec); + + /** + * TpCallChannel:hold-state: + * + * A #TpLocalHoldState specifying if the Call is currently held + * + * Since: 0.17.6 + */ + param_spec = g_param_spec_uint ("hold-state", "Hold State", + "The Hold state of the call", + 0, G_MAXUINT, TP_LOCAL_HOLD_STATE_UNHELD, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (gobject_class, PROP_HOLD_STATE, param_spec); + + + /** + * TpCallChannel:hold-state-reason: + * + * A #TpLocalHoldStateReason specifying why the Call is currently held. + * + * Since: 0.17.6 + */ + param_spec = g_param_spec_uint ("hold-state-reason", "Hold State Reason", + "The reason for the current hold state", + 0, G_MAXUINT, TP_LOCAL_HOLD_STATE_REASON_NONE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (gobject_class, PROP_HOLD_STATE_REASON, + param_spec); + + /** * TpCallChannel::content-added * @self: the #TpCallChannel @@ -953,7 +1093,8 @@ tp_call_channel_class_init (TpCallChannelClass *klass) * @state: the new #TpCallState * @flags: the new #TpCallFlags * @reason: the #TpCallStateReason for the change - * @details: additional details + * @details: (element-type utf8 GObject.Value): additional details as a + * #GHashTable readable using the tp_asv_* functions. * * The ::state-changed signal is emitted whenever the * call state changes. @@ -1231,6 +1372,28 @@ tp_call_channel_has_dtmf (TpCallChannel *self) return FALSE; } + +/** + * tp_call_channel_has_hold: + * @self: a #TpCallChannel + * + * Whether or not %self has the %TP_IFACE_CHANNEL_INTERFACE_HOLD + * interfaces + * + * Returns: whether or not @self supports Hold + * Since: 0.17.6 + */ +gboolean +tp_call_channel_has_hold (TpCallChannel *self) +{ + g_return_val_if_fail (TP_IS_CALL_CHANNEL (self), FALSE); + g_return_val_if_fail ( + tp_proxy_is_prepared (self, TP_CALL_CHANNEL_FEATURE_CORE), FALSE); + + return tp_proxy_has_interface_by_id (self, + TP_IFACE_QUARK_CHANNEL_INTERFACE_HOLD); +} + static void generic_async_cb (TpChannel *channel, const GError *error, @@ -1406,8 +1569,8 @@ tp_call_channel_accept_finish (TpCallChannel *self, void tp_call_channel_hangup_async (TpCallChannel *self, TpCallStateChangeReason reason, - gchar *detailed_reason, - gchar *message, + const gchar *detailed_reason, + const gchar *message, GAsyncReadyCallback callback, gpointer user_data) { @@ -1484,7 +1647,7 @@ add_content_cb (TpChannel *channel, */ void tp_call_channel_add_content_async (TpCallChannel *self, - gchar *name, + const gchar *name, TpMediaStreamType type, TpMediaStreamDirection initial_direction, GAsyncReadyCallback callback, @@ -1619,3 +1782,67 @@ tp_call_channel_send_tones_finish (TpCallChannel *self, { _tp_implement_finish_void (self, tp_call_channel_send_tones_async) } + +/** + * tp_call_channel_request_hold_async: + * @self: a #TpCallChannel + * @hold: Whether to request a hold or a unhold + * @callback: a callback to call when the operation finishes + * @user_data: data to pass to @callback + * + * Requests that the connection manager holds or unholds the call. Watch + * #TpCallChannel::hold-state property to know when the channel goes on + * hold or is unheld. Unholding may fail if the streaming implementation + * can not obtain all the resources needed to restart the call. + * + * Since: 0.17.6 + */ + +void +tp_call_channel_request_hold_async (TpCallChannel *self, + gboolean hold, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *result; + + g_return_if_fail (TP_IS_CALL_CHANNEL (self)); + + result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, + tp_call_channel_request_hold_async); + + if (tp_call_channel_has_hold (self)) + { + tp_cli_channel_interface_hold_call_request_hold (TP_CHANNEL (self), -1, + hold, generic_async_cb, g_object_ref (result), g_object_unref, + G_OBJECT (self)); + } + else + { + g_simple_async_result_set_error (result, + TP_ERRORS, TP_ERROR_NOT_CAPABLE, + "Channel does NOT implement the Hold interface"); + g_simple_async_result_complete_in_idle (result); + } + + g_object_unref (result); +} + + +/** + * tp_call_channel_request_hold_finish: + * @self: a #TpCallChannel + * @result: a #GAsyncResult + * @error: a #GError to fill + * + * Finishes tp_call_channel_request_hold_async + * + * Since: 0.17.6 + */ +gboolean +tp_call_channel_request_hold_finish (TpCallChannel *self, + GAsyncResult *result, + GError **error) +{ + _tp_implement_finish_void (self, tp_call_channel_request_hold_async); +} diff --git a/telepathy-glib/call-channel.h b/telepathy-glib/call-channel.h index 6193f4749..623963330 100644 --- a/telepathy-glib/call-channel.h +++ b/telepathy-glib/call-channel.h @@ -61,6 +61,7 @@ struct _TpCallStateReason TpHandle actor; TpCallStateChangeReason reason; gchar *dbus_reason; + gchar *message; /*<private>*/ guint ref_count; @@ -87,6 +88,7 @@ gboolean tp_call_channel_has_mutable_contents (TpCallChannel *self); GHashTable *tp_call_channel_get_members (TpCallChannel *self); gboolean tp_call_channel_has_dtmf (TpCallChannel *self); +gboolean tp_call_channel_has_hold (TpCallChannel *self); void tp_call_channel_set_ringing_async (TpCallChannel *self, GAsyncReadyCallback callback, @@ -111,8 +113,8 @@ gboolean tp_call_channel_accept_finish (TpCallChannel *self, void tp_call_channel_hangup_async (TpCallChannel *self, TpCallStateChangeReason reason, - gchar *detailed_reason, - gchar *message, + const gchar *detailed_reason, + const gchar *message, GAsyncReadyCallback callback, gpointer user_data); gboolean tp_call_channel_hangup_finish (TpCallChannel *self, @@ -120,7 +122,7 @@ gboolean tp_call_channel_hangup_finish (TpCallChannel *self, GError **error); void tp_call_channel_add_content_async (TpCallChannel *self, - gchar *name, + const gchar *name, TpMediaStreamType type, TpMediaStreamDirection initial_direction, GAsyncReadyCallback callback, @@ -138,6 +140,14 @@ gboolean tp_call_channel_send_tones_finish (TpCallChannel *self, GAsyncResult *result, GError **error); +void tp_call_channel_request_hold_async (TpCallChannel *self, + gboolean hold, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean tp_call_channel_request_hold_finish (TpCallChannel *self, + GAsyncResult *result, + GError **error); + G_END_DECLS #endif diff --git a/telepathy-glib/call-content-media-description.c b/telepathy-glib/call-content-media-description.c index ff83ba8ad..153d5787b 100644 --- a/telepathy-glib/call-content-media-description.c +++ b/telepathy-glib/call-content-media-description.c @@ -64,21 +64,26 @@ #include "telepathy-glib/util.h" #include "telepathy-glib/util-internal.h" -static void call_content_media_description_iface_init (gpointer, gpointer); +static void call_content_media_description_iface_init (gpointer iface, + gpointer data); +static void call_content_media_description_extra_iface_init (gpointer iface, + gpointer data); G_DEFINE_TYPE_WITH_CODE(TpCallContentMediaDescription, - tp_call_content_media_description, - G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CALL_CONTENT_MEDIA_DESCRIPTION, + tp_call_content_media_description, + G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CALL_CONTENT_MEDIA_DESCRIPTION, call_content_media_description_iface_init); - G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_DBUS_PROPERTIES, - tp_dbus_properties_mixin_iface_init); + G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_DBUS_PROPERTIES, + tp_dbus_properties_mixin_iface_init); + G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CALL_CONTENT_MEDIA_DESCRIPTION_INTERFACE_RTP_HEADER_EXTENSIONS, + call_content_media_description_extra_iface_init); + G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CALL_CONTENT_MEDIA_DESCRIPTION_INTERFACE_RTCP_FEEDBACK, + call_content_media_description_extra_iface_init); + G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CALL_CONTENT_MEDIA_DESCRIPTION_INTERFACE_RTCP_EXTENDED_REPORTS, + call_content_media_description_extra_iface_init); ); -static const gchar *tp_call_content_media_description_interfaces[] = { - NULL -}; - /* properties */ enum { @@ -90,7 +95,20 @@ enum PROP_HAS_REMOTE_INFORMATION, PROP_CODECS, PROP_REMOTE_CONTACT, - PROP_SSRCS + PROP_SSRCS, + + PROP_HEADER_EXTENSIONS, + + PROP_FEEDBACK_MESSAGES, + PROP_DOES_AVPF, + + PROP_LOSS_RLE_MAX_SIZE, + PROP_DUPLICATE_RLE_MAX_SIZE, + PROP_PACKET_RECEIPT_TIMES_MAX_SIZE, + PROP_DLRR_MAX_SIZE, + PROP_RTT_MODE, + PROP_STATISTIC_FLAGS, + PROP_ENABLE_METRICS, }; /* private structure */ @@ -99,6 +117,8 @@ struct _TpCallContentMediaDescriptionPrivate TpDBusDaemon *dbus_daemon; gchar *object_path; + /* GPtrArray of static strings, NULL-terminated */ + GPtrArray *interfaces; gboolean further_negotiation_required; gboolean has_remote_information; /* GPtrArray of owned GValueArray */ @@ -107,6 +127,18 @@ struct _TpCallContentMediaDescriptionPrivate /* TpHandle -> reffed GArray<uint> */ GHashTable *ssrcs; + /* GPtrArray of owned GValueArray (dbus-struct) */ + GPtrArray *header_extensions; + GHashTable *feedback_messages; + gboolean does_avpf; + guint loss_rle_max_size; + guint duplicate_rle_max_size; + guint packet_receipt_times_max_size; + guint dlrr_max_size; + TpRCPTXRRTTMode rtt_mode; + TpRTCPXRStatisticsFlags statistic_flags; + gboolean enable_metrics; + GSimpleAsyncResult *result; GCancellable *cancellable; guint handler_id; @@ -119,10 +151,18 @@ tp_call_content_media_description_init (TpCallContentMediaDescription *self) TP_TYPE_CALL_CONTENT_MEDIA_DESCRIPTION, TpCallContentMediaDescriptionPrivate); + self->priv->interfaces = g_ptr_array_new (); + g_ptr_array_add (self->priv->interfaces, NULL); + self->priv->ssrcs = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) g_array_unref); self->priv->codecs = g_ptr_array_new_with_free_func ( (GDestroyNotify) g_value_array_free); + + self->priv->header_extensions = g_ptr_array_new_with_free_func ( + (GDestroyNotify) g_value_array_free); + self->priv->feedback_messages = g_hash_table_new_full (NULL, NULL, NULL, + (GDestroyNotify) g_value_array_free); } static void @@ -136,6 +176,9 @@ tp_call_content_media_description_dispose (GObject *object) tp_clear_pointer (&self->priv->ssrcs, g_hash_table_unref); g_clear_object (&self->priv->dbus_daemon); + tp_clear_pointer (&self->priv->header_extensions, g_ptr_array_unref); + tp_clear_pointer (&self->priv->feedback_messages, g_hash_table_unref); + /* release any references held by the object here */ if (G_OBJECT_CLASS (tp_call_content_media_description_parent_class)->dispose) G_OBJECT_CLASS (tp_call_content_media_description_parent_class)->dispose ( @@ -148,6 +191,7 @@ tp_call_content_media_description_finalize (GObject *object) TpCallContentMediaDescription *self = (TpCallContentMediaDescription *) object; g_free (self->priv->object_path); + g_ptr_array_unref (self->priv->interfaces); G_OBJECT_CLASS (tp_call_content_media_description_parent_class)->finalize ( object); @@ -170,7 +214,7 @@ tp_call_content_media_description_get_property (GObject *object, g_value_set_object (value, self->priv->dbus_daemon); break; case PROP_INTERFACES: - g_value_set_boxed (value, tp_call_content_media_description_interfaces); + g_value_set_boxed (value, self->priv->interfaces->pdata); break; case PROP_FURTHER_NEGOTIATION_REQUIRED: g_value_set_boolean (value, self->priv->further_negotiation_required); @@ -187,6 +231,36 @@ tp_call_content_media_description_get_property (GObject *object, case PROP_SSRCS: g_value_set_boxed (value, self->priv->ssrcs); break; + case PROP_HEADER_EXTENSIONS: + g_value_set_boxed (value, self->priv->header_extensions); + break; + case PROP_FEEDBACK_MESSAGES: + g_value_set_boxed (value, self->priv->feedback_messages); + break; + case PROP_DOES_AVPF: + g_value_set_boolean (value, self->priv->does_avpf); + break; + case PROP_LOSS_RLE_MAX_SIZE: + g_value_set_uint (value, self->priv->loss_rle_max_size); + break; + case PROP_DUPLICATE_RLE_MAX_SIZE: + g_value_set_uint (value, self->priv->duplicate_rle_max_size); + break; + case PROP_PACKET_RECEIPT_TIMES_MAX_SIZE: + g_value_set_uint (value, self->priv->packet_receipt_times_max_size); + break; + case PROP_DLRR_MAX_SIZE: + g_value_set_uint (value, self->priv->dlrr_max_size); + break; + case PROP_RTT_MODE: + g_value_set_uint (value, self->priv->rtt_mode); + break; + case PROP_STATISTIC_FLAGS: + g_value_set_uint (value, self->priv->statistic_flags); + break; + case PROP_ENABLE_METRICS: + g_value_set_boolean (value, self->priv->enable_metrics); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; @@ -241,12 +315,46 @@ tp_call_content_media_description_class_init ( { "SSRCs", "ssrcs", NULL }, { NULL } }; + static TpDBusPropertiesMixinPropImpl rtp_header_extensions_props[] = { + { "HeaderExtensions", "header-extensions", NULL }, + { NULL } + }; + static TpDBusPropertiesMixinPropImpl rtcp_feedback_props[] = { + { "FeedbackMessages", "feedback-messages", NULL }, + { "DoesAVPF", "does-avpf", NULL }, + { NULL } + }; + static TpDBusPropertiesMixinPropImpl rtcp_extended_reports_props[] = { + { "LossRLEMaxSize", "loss-rle-max-size", NULL }, + { "DuplicateRLEMaxSize", "duplicate-rle-max-size", NULL }, + { "PacketReceiptTimesMaxSize", "packet-receipt-times-max-size", NULL }, + { "DLRRMaxSize", "dlrr-max-size", NULL }, + { "RTTMode", "rtt-mode", NULL }, + { "StatisticsFlags", "statistics-flags", NULL }, + { "EnableMetrics", "enable-metrics", NULL }, + { NULL } + }; static TpDBusPropertiesMixinIfaceImpl prop_interfaces[] = { { TP_IFACE_CALL_CONTENT_MEDIA_DESCRIPTION, tp_dbus_properties_mixin_getter_gobject_properties, NULL, media_description_props, }, + { TP_IFACE_CALL_CONTENT_MEDIA_DESCRIPTION_INTERFACE_RTP_HEADER_EXTENSIONS, + tp_dbus_properties_mixin_getter_gobject_properties, + NULL, + rtp_header_extensions_props, + }, + { TP_IFACE_CALL_CONTENT_MEDIA_DESCRIPTION_INTERFACE_RTCP_FEEDBACK, + tp_dbus_properties_mixin_getter_gobject_properties, + NULL, + rtcp_feedback_props, + }, + { TP_IFACE_CALL_CONTENT_MEDIA_DESCRIPTION_INTERFACE_RTCP_EXTENDED_REPORTS, + tp_dbus_properties_mixin_getter_gobject_properties, + NULL, + rtcp_extended_reports_props, + }, { NULL } }; @@ -378,6 +486,160 @@ tp_call_content_media_description_class_init ( G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_SSRCS, spec); + /** + * TpCallContentMediaDescription:header-extensions: + * + * A list of remote header extensions which are supported. + * + * Since: 0.17.6 + */ + spec = g_param_spec_boxed ("header-extensions", "Header Extentions", + "A list of remote header extensions which are supported.", + TP_ARRAY_TYPE_RTP_HEADER_EXTENSIONS_LIST, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_HEADER_EXTENSIONS, spec); + + /** + * TpCallContentMediaDescription:feedback-messages: + * + * A map of remote feedback codec properties that are supported. + * + * Since: 0.17.6 + */ + spec = g_param_spec_boxed ("feedback-messages", "Feedback Messages", + "A map of remote feedback codec properties that are supported.", + TP_HASH_TYPE_RTCP_FEEDBACK_MESSAGE_MAP, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_FEEDBACK_MESSAGES, spec); + + /** + * TpCallContentMediaDescription:does-avpf: + * + * %TRUE if the remote contact supports Audio-Visual Profile Feedback (AVPF), + * otherwise %FALSE. + * + * Since: 0.17.6 + */ + spec = g_param_spec_boolean ("does-avpf", "Does AVPF", + "True if the remote contact supports Audio-Visual Profile Feedback " + "(AVPF), otherwise False.", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_DOES_AVPF, spec); + + /** + * TpCallContentMediaDescription:loss-rle-max-size: + * + * If non-zero, enable Loss Run Length Encoded Report Blocks. The value of + * this integer represents the max-size of report blocks, as specified in + * RFC 3611 section 5.1. MAXUINT32 is used to indicate that there is no limit. + * + * Since: 0.17.6 + */ + spec = g_param_spec_uint ("loss-rle-max-size", "Loss RLE max size", + "If non-zero, enable Loss Run Length Encoded Report Blocks.", + 0, G_MAXUINT, 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_LOSS_RLE_MAX_SIZE, spec); + + /** + * TpCallContentMediaDescription:duplicate-rle-max-size: + * + * If non-zero, enable Duplicate Run-Length-Encoded Report Blocks. The value + * of this integer represents the max-size of report blocks, as specified in + * RFC 3611 section 5.1. MAXUINT32 is used to indicate that there is no limit. + * + * Since: 0.17.6 + */ + spec = g_param_spec_uint ("duplicate-rle-max-size", + "Duplicate Run-Length-Encoded max size", + "If non-zero, enable Duplicate Run-Length-Encoded Report Blocks.", + 0, G_MAXUINT, 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_DUPLICATE_RLE_MAX_SIZE, + spec); + + /** + * TpCallContentMediaDescription:packet-receipt-times-max-size: + * + * If non-zero, enable Packet Receipt Times Report Blocks. The value of this + * integer represents the max-size of report blocks, as specified in RFC 3611 + * section 5.1. MAXUINT32 is used to indicate that there is no limit. + * + * Since: 0.17.6 + */ + spec = g_param_spec_uint ("packet-receipt-times-max-size", + "Packet Receipt Times max size", + "If non-zero, enable Packet Receipt Times Report Blocks.", + 0, G_MAXUINT, 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, + PROP_PACKET_RECEIPT_TIMES_MAX_SIZE, spec); + + /** + * TpCallContentMediaDescription:dlrr-max-size: + * + * If non-zero, enable Receiver Reference Time and Delay since Last Receiver + * Report Blocks (for estimating Round Trip Times between non-senders and + * other parties in the call. The value of this integer represents the + * max-size of report blocks, as specified in RFC 3611 section 5.1. MAXUINT32 + * is used to indicate that there is no limit. + * + * Since: 0.17.6 + */ + spec = g_param_spec_uint ("dlrr-max-size", + "Receiver Reference Time and Delay since Last Receiver max size", + "If non-zero, enable Receiver Reference Time and Delay since Last " + "Receiver Report Blocks.", + 0, G_MAXUINT, 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_DLRR_MAX_SIZE, spec); + + /** + * TpCallContentMediaDescription:rtt-mode: + * + * Who is allowed to send Delay since Last Receiver Reports. Value from + * #TpRCPTXRRTTMode. + * + * Since: 0.17.6 + */ + spec = g_param_spec_uint ("rtt-mode", "RTT Mode", + "Who is allowed to send Delay since Last Receiver Reports.", + 0, G_MAXUINT, 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_RTT_MODE, spec); + + /** + * TpCallContentMediaDescription:statistics-flags: + * + * Which fields SHOULD be included in the statistics summary report blocks + * that are sent, and whether to send VoIP Metrics Report Blocks. There can + * be zero or more flags set. Value from #TpRTCPXRStatisticsFlags. + * + * Since: 0.17.6 + */ + spec = g_param_spec_uint ("statistics-flags", "Statistics Flags", + "Which fields SHOULD be included in the statistics summary report blocks " + "that are sent, and whether to send VoIP Metrics Report Blocks.", + 0, G_MAXUINT, 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_STATISTIC_FLAGS, spec); + + /** + * TpCallContentMediaDescription:enable-metrics: + * + * Whether to enable VoIP Metrics Report Blocks. These blocks are of a fixed + * size. + * + * Since: 0.17.6 + */ + spec = g_param_spec_boolean ("enable-metrics", "Enable Metrics", + "Whether to enable VoIP Metrics Report Blocks. These blocks are of a " + "fixed size.", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_ENABLE_METRICS, spec); + klass->dbus_props_class.interfaces = prop_interfaces; tp_dbus_properties_mixin_class_init (object_class, G_STRUCT_OFFSET (TpCallContentMediaDescriptionClass, dbus_props_class)); @@ -462,10 +724,12 @@ tp_call_content_media_description_get_remote_contact ( /** * tp_call_content_media_description_add_ssrc: * @self: a #TpCallContentMediaDescription - * @contact: if you use this API, you know what it is about - * @ssrc: if you use this API, you know what it is about + * @contact: The #TpHandle of a contact that is part of the call + * @ssrc: A SSRC that this contact may send from * - * if you use this API, you know what it is about + * Add an SSRC to the list of SSRCs that a contact will send from. A SSRC + * is a synchronization source in RTP, it is the identifier for a continuous + * stream of packets following the same timeline. * * Since: 0.17.5 */ @@ -501,12 +765,13 @@ tp_call_content_media_description_add_ssrc (TpCallContentMediaDescription *self, /** * tp_call_content_media_description_append_codec: * @self: a #TpCallContentMediaDescription - * @identifier: if you use this API, you know what it is about - * @name: if you use this API, you know what it is about - * @clock_rate: if you use this API, you know what it is about - * @channels: if you use this API, you know what it is about - * @updated: if you use this API, you know what it is about - * @parameters: if you use this API, you know what it is about + * @identifier: Numeric identifier for the codec. This will be used as the PT + * in the SDP or content description. + * @name: The name of the codec. + * @clock_rate: The clock rate of the codec. + * @channels: Number of channels of the codec if applicable, otherwise 0. + * @updated: %TRUE if this codec was updated since the last Media Description + * @parameters: a #GHashTable of string->string containing optional parameters * * Add description for a supported codec. * @@ -538,6 +803,304 @@ tp_call_content_media_description_append_codec ( } static void +add_interface (TpCallContentMediaDescription *self, + const gchar *interface) +{ + if (tp_g_ptr_array_contains (self->priv->interfaces, (gchar *) interface)) + return; + + /* Remove terminating NULL, add interface, then add the NULL back */ + g_ptr_array_remove_index_fast (self->priv->interfaces, + self->priv->interfaces->len - 1); + g_ptr_array_add (self->priv->interfaces, (gchar *) interface); + g_ptr_array_add (self->priv->interfaces, NULL); +} + +/** + * tp_call_content_media_description_add_rtp_header_extensions_interface: + * @self: a #TpCallContentMediaDescription + * + * Adds the RTPHeaderExtensions interface to the list of supported interfaces + * + * Since: 0.17.6 + */ + +void +tp_call_content_media_description_add_rtp_header_extensions_interface ( + TpCallContentMediaDescription *self) +{ + add_interface (self, + TP_IFACE_CALL_CONTENT_MEDIA_DESCRIPTION_INTERFACE_RTP_HEADER_EXTENSIONS); +} + + +/** + * tp_call_content_media_description_add_rtcp_feedback_interface: + * @self: a #TpCallContentMediaDescription + * + * Adds the RTCPFeedback interface to the list of supported interfaces + * + * Since: 0.17.6 + */ + +void +tp_call_content_media_description_add_rtcp_feedback_interface ( + TpCallContentMediaDescription *self) +{ + add_interface (self, + TP_IFACE_CALL_CONTENT_MEDIA_DESCRIPTION_INTERFACE_RTCP_FEEDBACK); +} + + +/** + * tp_call_content_media_description_add_rtcp_extended_reports_interface: + * @self: a #TpCallContentMediaDescription + * + * Adds the RTCPExtendedReports interface to the list of supported interfaces + * + * Since: 0.17.6 + */ + +void +tp_call_content_media_description_add_rtcp_extended_reports_interface ( + TpCallContentMediaDescription *self) +{ + add_interface (self, + TP_IFACE_CALL_CONTENT_MEDIA_DESCRIPTION_INTERFACE_RTCP_EXTENDED_REPORTS); +} + + +/** + * tp_call_content_media_description_add_rtp_header_extension: + * @self: a #TpCallContentMediaDescription + * @id: identifier to be negotiated. + * @direction: a #TpMediaStreamDirection in which the Header Extension is + * negotiated. + * @uri: URI defining the extension. + * @parameters: Feedback parameters as a string. Format is defined in the + * relevant RFC. + * + * Add an element to the #TpCallContentMediaDescription:rtp-header-extensions + * property. + * + * Implement + * %TP_IFACE_CALL_CONTENT_MEDIA_DESCRIPTION_INTERFACE_RTP_HEADER_EXTENSIONS + * interface. + * + * Since: 0.17.6 + */ +void +tp_call_content_media_description_add_rtp_header_extension ( + TpCallContentMediaDescription *self, + guint id, + TpMediaStreamDirection direction, + const gchar *uri, + const gchar *parameters) +{ + g_return_if_fail (TP_IS_CALL_CONTENT_MEDIA_DESCRIPTION (self)); + + g_ptr_array_add (self->priv->header_extensions, tp_value_array_build (4, + G_TYPE_UINT, id, + G_TYPE_UINT, direction, + G_TYPE_STRING, uri, + G_TYPE_STRING, parameters, + G_TYPE_INVALID)); + + tp_call_content_media_description_add_rtp_header_extensions_interface (self); +} + +static GValueArray * +ensure_rtcp_feedback_properties (TpCallContentMediaDescription *self, + guint codec_identifier) +{ + GValueArray *properties; + GPtrArray *messages_array; + + properties = g_hash_table_lookup (self->priv->feedback_messages, + GUINT_TO_POINTER (codec_identifier)); + + if (properties == NULL) + { + messages_array = g_ptr_array_new_with_free_func ( + (GDestroyNotify) g_value_array_free); + properties = tp_value_array_build (2, + G_TYPE_UINT, G_MAXUINT, + G_TYPE_PTR_ARRAY, messages_array, + G_TYPE_INVALID); + + g_hash_table_insert (self->priv->feedback_messages, + GUINT_TO_POINTER (codec_identifier), properties); + + g_ptr_array_unref (messages_array); + } + + return properties; +} + +/** + * tp_call_content_media_description_add_rtcp_feedback_message: + * @self: a #TpCallContentMediaDescription + * @codec_identifier: Numeric identifier for the codec. This will be used as the + * PT in the SDP or content description. + * @type: feedback type, for example "ack", "nack", or "ccm". + * @subtype: feedback subtype, according to the Type, can be an empty string + * (""), if there is no subtype. For example, generic nack is Type="nack" + * Subtype="". + * @parameters: feedback parameters as a string. Format is defined in the + * relevant RFC. + * + * Add a message for a given codec. This ensures @codec_identifier is + * in the #TpCallContentMediaDescription:feedback-messages map. The + * rtcp-minimum-interval is set to %G_MAXUINT and can then be changed using + * tp_call_content_media_description_set_rtcp_feedback_minimum_interval(). + * + * Implement + * %TP_IFACE_CALL_CONTENT_MEDIA_DESCRIPTION_INTERFACE_RTCP_FEEDBACK + * interface. + * + * Since: 0.17.6 + */ +void +tp_call_content_media_description_add_rtcp_feedback_message ( + TpCallContentMediaDescription *self, + guint codec_identifier, + const gchar *type, + const gchar *subtype, + const gchar *parameters) +{ + GValueArray *properties; + GValue *value; + GPtrArray *messages_array; + + g_return_if_fail (TP_IS_CALL_CONTENT_MEDIA_DESCRIPTION (self)); + + properties = ensure_rtcp_feedback_properties (self, codec_identifier); + value = g_value_array_get_nth (properties, 1); + messages_array = g_value_get_boxed (value); + + g_ptr_array_add (messages_array, tp_value_array_build (3, + G_TYPE_STRING, type, + G_TYPE_STRING, subtype, + G_TYPE_STRING, parameters, + G_TYPE_INVALID)); + + tp_call_content_media_description_add_rtcp_feedback_interface (self); +} + +/** + * tp_call_content_media_description_set_rtcp_feedback_minimum_interval: + * @self: a #TpCallContentMediaDescription + * @codec_identifier: Numeric identifier for the codec. This will be used as the + * PT in the SDP or content description. + * @rtcp_minimum_interval: The minimum interval between two regular RTCP packets + * in milliseconds for this content. If no special value is desired, one should + * put MAXUINT (0xFFFFFFFF). Implementors and users of Call's RTCPFeedback + * should not use the MAXUINT default. Instead, in RTP/AVP, the default should + * be 5000 (5 seconds). If using the RTP/AVPF profile, it can be set to a lower + * value, the default being 0. + * + * Set the minimum interval for a given codec. This ensures @codec_identifier is + * in the #TpCallContentMediaDescription:feedback-messages map. The messages + * can then be added using + * tp_call_content_media_description_add_rtcp_feedback_message(). + * + * Implement + * %TP_IFACE_CALL_CONTENT_MEDIA_DESCRIPTION_INTERFACE_RTCP_FEEDBACK + * interface. + * + * Since: 0.17.6 + */ +void +tp_call_content_media_description_set_rtcp_feedback_minimum_interval ( + TpCallContentMediaDescription *self, + guint codec_identifier, + guint rtcp_minimum_interval) +{ + GValueArray *properties; + GValue *value; + + g_return_if_fail (TP_IS_CALL_CONTENT_MEDIA_DESCRIPTION (self)); + + properties = ensure_rtcp_feedback_properties (self, codec_identifier); + value = g_value_array_get_nth (properties, 0); + g_value_set_uint (value, rtcp_minimum_interval); + + tp_call_content_media_description_add_rtcp_feedback_interface (self); +} + +/** + * tp_call_content_media_description_set_does_avpf: + * @self: a #TpCallContentMediaDescription + * @does_avpf: the value for + * #TpCallContentMediaDescription:does-avpf property. + * + * Implement properties for + * %TP_IFACE_CALL_CONTENT_MEDIA_DESCRIPTION_INTERFACE_RTCP_FEEDBACK + * interface + * + * Since: 0.17.6 + */ +void +tp_call_content_media_description_set_does_avpf ( + TpCallContentMediaDescription *self, + gboolean does_avpf) +{ + g_return_if_fail (TP_IS_CALL_CONTENT_MEDIA_DESCRIPTION (self)); + + self->priv->does_avpf = does_avpf; + + tp_call_content_media_description_add_rtcp_feedback_interface (self); +} + +/** + * tp_call_content_media_description_set_rtcp_extended_reports: + * @self: a #TpCallContentMediaDescription + * @loss_rle_max_size: the value for + * #TpCallContentMediaDescription:loss-rle-max-size property. + * @duplicate_rle_max_size: the value for + * #TpCallContentMediaDescription:duplicate-rle-max-size property. + * @packet_receipt_times_max_size: the value for + * #TpCallContentMediaDescription:packet-receipt-times-max-size property. + * @dlrr_max_size: the value for + * #TpCallContentMediaDescription:dlrr-max-size property. + * @rtt_mode: the value for + * #TpCallContentMediaDescription:rtt-mpde property. + * @statistic_flags: the value for + * #TpCallContentMediaDescription:statistic-flags property. + * @enable_metrics: the value for + * #TpCallContentMediaDescription:enable-metrics property. + * + * Implement + * %TP_IFACE_CALL_CONTENT_MEDIA_DESCRIPTION_INTERFACE_RTCP_EXTENDED_REPORTS + * interface. + * + * Since: 0.17.6 + */ +void +tp_call_content_media_description_set_rtcp_extended_reports ( + TpCallContentMediaDescription *self, + guint loss_rle_max_size, + guint duplicate_rle_max_size, + guint packet_receipt_times_max_size, + guint dlrr_max_size, + TpRCPTXRRTTMode rtt_mode, + TpRTCPXRStatisticsFlags statistic_flags, + gboolean enable_metrics) +{ + g_return_if_fail (TP_IS_CALL_CONTENT_MEDIA_DESCRIPTION (self)); + + self->priv->loss_rle_max_size = loss_rle_max_size; + self->priv->duplicate_rle_max_size = duplicate_rle_max_size; + self->priv->packet_receipt_times_max_size = packet_receipt_times_max_size; + self->priv->dlrr_max_size = dlrr_max_size; + self->priv->rtt_mode = rtt_mode; + self->priv->statistic_flags = statistic_flags; + self->priv->enable_metrics = enable_metrics; + + tp_call_content_media_description_add_rtcp_extended_reports_interface (self); +} + +static void cancelled_cb (GCancellable *cancellable, gpointer user_data) { @@ -601,7 +1164,7 @@ _tp_call_content_media_description_dup_properties ( return tp_asv_new ( TP_PROP_CALL_CONTENT_MEDIA_DESCRIPTION_INTERFACES, - G_TYPE_STRV, tp_call_content_media_description_interfaces, + G_TYPE_STRV, self->priv->interfaces->pdata, TP_PROP_CALL_CONTENT_MEDIA_DESCRIPTION_FURTHER_NEGOTIATION_REQUIRED, G_TYPE_BOOLEAN, self->priv->further_negotiation_required, TP_PROP_CALL_CONTENT_MEDIA_DESCRIPTION_HAS_REMOTE_INFORMATION, @@ -714,3 +1277,8 @@ call_content_media_description_iface_init (gpointer iface, gpointer data) IMPLEMENT(reject); #undef IMPLEMENT } + +static void +call_content_media_description_extra_iface_init (gpointer iface, gpointer data) +{ +} diff --git a/telepathy-glib/call-content-media-description.h b/telepathy-glib/call-content-media-description.h index 6d6af6cc3..2ea3e790b 100644 --- a/telepathy-glib/call-content-media-description.h +++ b/telepathy-glib/call-content-media-description.h @@ -98,6 +98,49 @@ void tp_call_content_media_description_append_codec ( gboolean updated, GHashTable *parameters); +void tp_call_content_media_description_add_rtp_header_extension ( + TpCallContentMediaDescription *self, + guint id, + TpMediaStreamDirection direction, + const gchar *uri, + const gchar *parameters); + +void tp_call_content_media_description_add_rtcp_feedback_message ( + TpCallContentMediaDescription *self, + guint codec_identifier, + const gchar *type, + const gchar *subtype, + const gchar *parameters); + +void tp_call_content_media_description_set_rtcp_feedback_minimum_interval ( + TpCallContentMediaDescription *self, + guint codec_identifier, + guint rtcp_minimum_interval); + +void tp_call_content_media_description_set_does_avpf ( + TpCallContentMediaDescription *self, + gboolean does_avpf); + +void tp_call_content_media_description_set_rtcp_extended_reports ( + TpCallContentMediaDescription *self, + guint loss_rle_max_size, + guint duplicate_rle_max_size, + guint packet_receipt_times_max_size, + guint dlrr_max_size, + TpRCPTXRRTTMode rtt_mode, + TpRTCPXRStatisticsFlags statistic_flags, + gboolean enable_metrics); + +void tp_call_content_media_description_add_rtp_header_extensions_interface ( + TpCallContentMediaDescription *self); + +void tp_call_content_media_description_add_rtcp_feedback_interface ( + TpCallContentMediaDescription *self); + +void tp_call_content_media_description_add_rtcp_extended_reports_interface ( + TpCallContentMediaDescription *self); + + G_END_DECLS #endif /* #ifndef __TP_CALL_CONTENT_MEDIA_DESCRIPTION_H__*/ diff --git a/telepathy-glib/call-content.c b/telepathy-glib/call-content.c index 129213222..ae3360a5f 100644 --- a/telepathy-glib/call-content.c +++ b/telepathy-glib/call-content.c @@ -47,6 +47,7 @@ #include "telepathy-glib/call-content.h" +#include <telepathy-glib/call-channel.h> #include <telepathy-glib/call-misc.h> #include <telepathy-glib/call-stream.h> #include <telepathy-glib/cli-call.h> @@ -75,6 +76,7 @@ typedef struct _SendTonesData SendTonesData; struct _TpCallContentPrivate { TpConnection *connection; + TpCallChannel *channel; gchar *name; TpMediaStreamType media_type; @@ -93,7 +95,8 @@ enum PROP_NAME, PROP_MEDIA_TYPE, PROP_DISPOSITION, - PROP_STREAMS + PROP_STREAMS, + PROP_CHANNEL }; enum @@ -116,6 +119,7 @@ _tp_call_stream_new (TpCallContent *self, "dbus-connection", tp_proxy_get_dbus_connection (self), "object-path", object_path, "connection", self->priv->connection, + "content", self, NULL); } @@ -481,6 +485,9 @@ tp_call_content_get_property (GObject *object, case PROP_STREAMS: g_value_set_boxed (value, self->priv->streams); break; + case PROP_CHANNEL: + g_value_set_object (value, self->priv->channel); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; @@ -501,6 +508,10 @@ tp_call_content_set_property (GObject *object, g_assert (self->priv->connection == NULL); /* construct-only */ self->priv->connection = g_value_dup_object (value); break; + case PROP_CHANNEL: + g_assert (self->priv->channel == NULL); /* construct-only */ + self->priv->channel = g_value_dup_object (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; @@ -629,6 +640,21 @@ tp_call_content_class_init (TpCallContentClass *klass) param_spec); /** + * TpCallContent:channel: + * + * The parent #TpCallChannel of the content. + * + * Since: 0.17.6 + */ + param_spec = g_param_spec_object ("channel", "Channel", + "The channel of this content", + TP_TYPE_CALL_CHANNEL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (gobject_class, PROP_CHANNEL, + param_spec); + + + /** * TpCallContent::removed * @self: the #TpCallContent * diff --git a/telepathy-glib/call-stream.c b/telepathy-glib/call-stream.c index 7d2627dd1..67926e147 100644 --- a/telepathy-glib/call-stream.c +++ b/telepathy-glib/call-stream.c @@ -47,6 +47,7 @@ #include "telepathy-glib/call-stream.h" +#include <telepathy-glib/call-content.h> #include <telepathy-glib/call-misc.h> #include <telepathy-glib/cli-call.h> #include <telepathy-glib/cli-misc.h> @@ -72,6 +73,8 @@ struct _TpCallStreamPrivate { TpConnection *connection; + TpCallContent *content; + /* TpContact -> TpSendingState */ GHashTable *remote_members; TpSendingState local_sending_state; @@ -85,6 +88,7 @@ enum PROP_CONNECTION = 1, PROP_LOCAL_SENDING_STATE, PROP_CAN_REQUEST_RECEIVING, + PROP_CONTENT }; enum /* signals */ @@ -242,6 +246,7 @@ tp_call_stream_dispose (GObject *object) { TpCallStream *self = (TpCallStream *) object; + g_clear_object (&self->priv->content); g_clear_object (&self->priv->connection); tp_clear_pointer (&self->priv->remote_members, g_hash_table_unref); @@ -268,6 +273,9 @@ tp_call_stream_get_property (GObject *object, case PROP_CAN_REQUEST_RECEIVING: g_value_set_boolean (value, priv->can_request_receiving); break; + case PROP_CONTENT: + g_value_set_object (value, self->priv->content); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; @@ -288,6 +296,10 @@ tp_call_stream_set_property (GObject *object, g_assert (self->priv->connection == NULL); /* construct-only */ self->priv->connection = g_value_dup_object (value); break; + case PROP_CONTENT: + g_assert (self->priv->content == NULL); /* construct-only */ + self->priv->content = g_value_dup_object (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; @@ -343,7 +355,7 @@ tp_call_stream_class_init (TpCallStreamClass *klass) * Since: 0.17.5 */ param_spec = g_param_spec_object ("connection", "Connection", - "The connection of this content", + "The connection of this stream", TP_TYPE_CONNECTION, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); g_object_class_install_property (gobject_class, PROP_CONNECTION, @@ -381,6 +393,21 @@ tp_call_stream_class_init (TpCallStreamClass *klass) param_spec); /** + * TpCallStream:content: + * + * The Content that this streams belongs to + * + * Since: 0.17.6 + */ + param_spec = g_param_spec_object ("content", + "Content", + "The content that this Stream belongs to", + TP_TYPE_CALL_CONTENT, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (gobject_class, PROP_CONTENT, + param_spec); + + /** * TpCallStream::local-sending-state-changed * @self: the #TpCallStream * @state: the new #TpSendingState diff --git a/telepathy-glib/capabilities.c b/telepathy-glib/capabilities.c index e46bc20cc..66140b279 100644 --- a/telepathy-glib/capabilities.c +++ b/telepathy-glib/capabilities.c @@ -426,7 +426,7 @@ supports_call_full (TpCapabilities *self, * @handle_type as TargetHandleType, a True value for InitialAudio and an * identifier of the appropriate type can be expected to work, %FALSE otherwise. * - * Since: 0.UNRELEASED + * Since: 0.17.6 */ gboolean tp_capabilities_supports_audio_call (TpCapabilities *self, @@ -450,7 +450,7 @@ tp_capabilities_supports_audio_call (TpCapabilities *self, * expected to work, * %FALSE otherwise. * - * Since: 0.UNRELEASED + * Since: 0.17.6 */ gboolean tp_capabilities_supports_audio_video_call (TpCapabilities *self, @@ -470,7 +470,7 @@ tp_capabilities_supports_audio_video_call (TpCapabilities *self, * HandleTypeContact as TargetHandleType and a contact identifier can be * expected to work, %FALSE otherwise. * - * Since: 0.UNRELEASED + * Since: 0.17.6 */ gboolean tp_capabilities_supports_file_transfer (TpCapabilities *self) diff --git a/telepathy-glib/channel.c b/telepathy-glib/channel.c index afde2d0c1..5a27879e9 100644 --- a/telepathy-glib/channel.c +++ b/telepathy-glib/channel.c @@ -1872,12 +1872,12 @@ tp_channel_join_async (TpChannel *self, /** * tp_channel_join_finish: * @self: a #TpChannel - * @result: a #GAsyncResult + * @result: a #GAsyncResult passed to the callback for tp_channel_join_async(). * @error: a #GError to fill * - * Finishes to join a channel. + * Completes a call to tp_channel_join_async(). * - * Returns: %TRUE if the channel has been joined; %FALSE otherwise + * Returns: %TRUE if the channel was successfully joined; %FALSE otherwise * * Since: 0.15.5 */ @@ -2080,10 +2080,10 @@ tp_channel_leave_async (TpChannel *self, /** * tp_channel_leave_finish: * @self: a #TpChannel - * @result: a #GAsyncResult + * @result: a #GAsyncResult passed to the callback for tp_channel_leave_async(). * @error: a #GError to fill * - * Finishes to leave a channel. + * Completes a call to tp_channel_leave_async(). * * Returns: %TRUE if the channel has been left; %FALSE otherwise * @@ -2137,10 +2137,10 @@ tp_channel_close_async (TpChannel *self, /** * tp_channel_close_finish: * @self: a #TpChannel - * @result: a #GAsyncResult + * @result: a #GAsyncResult passed to the callback for tp_channel_close_async(). * @error: a #GError to fill * - * Finishes to close a channel. + * Finishes a call to tp_channel_leave_async(). * * Returns: %TRUE if the channel has been closed; %FALSE otherwise * @@ -2222,10 +2222,10 @@ tp_channel_destroy_async (TpChannel *self, /** * tp_channel_destroy_finish: * @self: a #TpChannel - * @result: a #GAsyncResult + * @result: a #GAsyncResult passed to the callback for tp_channel_destroy_async(). * @error: a #GError to fill * - * Finishes to leave a channel. + * Completes a call to tp_channel_destroy_async(). * * Returns: %TRUE if the channel has been destroyed or closed; %FALSE otherwise * @@ -2338,10 +2338,12 @@ tp_channel_provide_password_async (TpChannel *self, /** * tp_channel_provide_password_finish: * @self: a #TpChannel - * @result: a #GAsyncResult + * @result: a #GAsyncResult passed to the callback for + * tp_channel_provide_password_async(). * @error: a #GError to fill * - * Finishes to provide a password. If the password was rejected, the operation + * Completes a call to tp_channel_provide_password_async(). + * If the password was rejected, the operation * fails with #TP_ERROR_AUTHENTICATION_FAILED. * * Returns: %TRUE if the password has been provided and accepted, diff --git a/telepathy-glib/connection-manager.c b/telepathy-glib/connection-manager.c index d02f027d2..b8c76857a 100644 --- a/telepathy-glib/connection-manager.c +++ b/telepathy-glib/connection-manager.c @@ -554,7 +554,7 @@ finally: * @error is %NULL and @cm is considered to be ready. Otherwise, @error is * non-%NULL and @cm is not ready. * - * Deprecated: since 0.UNRELEASED, use tp_proxy_prepare_async() instead + * Deprecated: since 0.17.6, use tp_proxy_prepare_async() instead */ /** @@ -571,7 +571,7 @@ finally: * supported protocols and parameters has been retrieved. * * Since: 0.7.26 - * Deprecated: since 0.UNRELEASED, use tp_proxy_prepare_async() instead + * Deprecated: since 0.17.6, use tp_proxy_prepare_async() instead */ void tp_connection_manager_call_when_ready (TpConnectionManager *self, @@ -1776,7 +1776,7 @@ list_connection_managers_async_cb (TpConnectionManager * const *cms, * asynchronously, and wait for their %TP_CONNECTION_MANAGER_FEATURE_CORE * feature to be ready. * - * Since: 0.UNRELEASED + * Since: 0.17.6 */ void tp_list_connection_managers_async (TpDBusDaemon *dbus_daemon, @@ -1820,7 +1820,7 @@ tp_list_connection_managers_async (TpDBusDaemon *dbus_daemon, * * Returns: (transfer full) (element-type TelepathyGLib.ConnectionManager): a * newly allocated list of references to #TpConnectionManager objects - * Since: 0.UNRELEASED + * Since: 0.17.6 */ GList * tp_list_connection_managers_finish (GAsyncResult *result, @@ -1965,7 +1965,7 @@ tp_connection_manager_get_name (TpConnectionManager *self) * Returns: %TRUE, unless the #TpConnectionManager:info-source property is * %TP_CM_INFO_SOURCE_NONE * Since: 0.7.26 - * Deprecated: since 0.UNRELEASED, use tp_proxy_is_prepared() + * Deprecated: since 0.17.6, use tp_proxy_is_prepared() * with %TP_CONNECTION_MANAGER_FEATURE_CORE instead */ gboolean @@ -2154,7 +2154,7 @@ tp_connection_manager_get_protocol_object (TpConnectionManager *self, * of #TpProtocol objects representing the protocols supported by @self, * owned by the caller * - * Since: 0.UNRELEASED + * Since: 0.17.6 */ GList * tp_connection_manager_dup_protocols (TpConnectionManager *self) diff --git a/telepathy-glib/contact.c b/telepathy-glib/contact.c index 11007ef85..f438b7f6b 100644 --- a/telepathy-glib/contact.c +++ b/telepathy-glib/contact.c @@ -33,6 +33,7 @@ #include <telepathy-glib/util.h> #define DEBUG_FLAG TP_DEBUG_CONTACTS +#include "telepathy-glib/base-contact-list-internal.h" #include "telepathy-glib/connection-contact-list.h" #include "telepathy-glib/connection-internal.h" #include "telepathy-glib/contact-internal.h" @@ -2652,10 +2653,11 @@ contact_set_subscription_states (TpContact *self, if (publish_request == NULL) publish_request = ""; - DEBUG ("contact#%u state changed:", self->priv->handle); - DEBUG (" subscribe: %d", subscribe); - DEBUG (" publish: %d", publish); - DEBUG (" publish request: %s", publish_request); + DEBUG ("contact#%u state changed: subscribe=%c publish=%c '%s'", + self->priv->handle, + _tp_base_contact_list_presence_state_to_letter (subscribe), + _tp_base_contact_list_presence_state_to_letter (publish), + publish_request); self->priv->has_features |= CONTACT_FEATURE_FLAG_STATES; diff --git a/telepathy-glib/dbus-tube-channel.c b/telepathy-glib/dbus-tube-channel.c index 08748676f..2680fae3a 100644 --- a/telepathy-glib/dbus-tube-channel.c +++ b/telepathy-glib/dbus-tube-channel.c @@ -21,10 +21,44 @@ /** * SECTION:dbus-tube-channel * @title: TpDBusTubeChannel - * @short_description: proxy object for a dbus tube channel + * @short_description: proxy object for D-Bus tube channels * - * #TpDBusTubeChannel is a sub-class of #TpChannel providing convenient API - * to offer and accept a dbus tube. + * #TpDBusTubeChannel provides API for working with D-Bus tube channels, which + * allow applications to open D-Bus connections to a contact or chat room. + * + * To create a new outgoing D-Bus tube channel, do something like: + * + * |[ + * GHashTable *request_properties = tp_asv_new ( + * TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING, TP_IFACE_CHANNEL_TYPE_DBUS_TUBE, + * TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, G_TYPE_UINT, TP_HANDLE_TYPE_CONTACT, + * TP_PROP_CHANNEL_TARGET_ID, G_TYPE_STRING, tp_contact_get_identifier (contact), + * TP_PROP_CHANNEL_TYPE_DBUS_TUBE_SERVICE_NAME, G_TYPE_STRING, "com.example.walrus", + * NULL); + * TpAccountChannelRequest *req = tp_account_channel_request_new (account, + * request_properties, TP_USER_ACTION_TIME_NOT_USER_ACTION); + * tp_account_channel_request_create_and_handle_channel_async (req, NULL, callback, NULL); + * + * // ... + * + * static void + * callback ( + * GObject *source, + * GAsyncResult *result, + * gpointer user_data) + * { + * TpAccountChannelRequest *req = TP_ACCOUNT_CHANNEL_REQUEST (source); + * TpChannel *channel; + * GError *error = NULL; + * + * channel = tp_account_channel_request_create_and_handle_channel_finish (req, result, &error); + * tp_dbus_tube_channel_offer_async (TP_DBUS_TUBE_CHANNEL (channel), NULL, offer_callback, NULL); + * } + * ]| + * + * You can find a fuller example in the <ulink + * url="http://cgit.freedesktop.org/telepathy/telepathy-glib/tree/examples/client/dbus-tubes/">examples/client/dbus-tubes</ulink> + * directory. * * Since: 0.15.6 */ @@ -49,6 +83,8 @@ #include "telepathy-glib/dbus-tube-channel.h" +#include <telepathy-glib/cli-channel.h> +#include <telepathy-glib/cli-misc.h> #include <telepathy-glib/contact.h> #include <telepathy-glib/dbus.h> #include <telepathy-glib/enums.h> @@ -71,6 +107,10 @@ G_DEFINE_TYPE (TpDBusTubeChannel, tp_dbus_tube_channel, TP_TYPE_CHANNEL) struct _TpDBusTubeChannelPrivate { GHashTable *parameters; + TpTubeChannelState state; + + GSimpleAsyncResult *result; + gchar *address; }; enum @@ -85,6 +125,9 @@ tp_dbus_tube_channel_dispose (GObject *obj) TpDBusTubeChannel *self = (TpDBusTubeChannel *) obj; tp_clear_pointer (&self->priv->parameters, g_hash_table_unref); + /* If priv->result isn't NULL, it owns a ref to self. */ + g_warn_if_fail (self->priv->result == NULL); + tp_clear_pointer (&self->priv->address, g_free); G_OBJECT_CLASS (tp_dbus_tube_channel_parent_class)->dispose (obj); } @@ -115,6 +158,102 @@ tp_dbus_tube_channel_get_property (GObject *object, } static void +complete_operation (TpDBusTubeChannel *self) +{ + TpDBusTubeChannelPrivate *priv = self->priv; + GSimpleAsyncResult *result = priv->result; + + /* This dance is to ensure that we don't accidentally manipulate priv->result + * while calling out to user code. For instance, someone might call + * tp_proxy_invalidate() on us, which winds up landing us in here via our + * handler for that signal. + */ + g_assert (priv->result != NULL); + result = priv->result; + priv->result = NULL; + g_simple_async_result_complete (result); + g_object_unref (result); +} + +static void +dbus_connection_new_cb (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + TpDBusTubeChannel *self = user_data; + GDBusConnection *conn; + GError *error = NULL; + + conn = g_dbus_connection_new_for_address_finish (result, &error); + if (conn == NULL) + { + DEBUG ("Failed to create GDBusConnection: %s", error->message); + g_simple_async_result_take_error (self->priv->result, error); + } + else + { + g_simple_async_result_set_op_res_gpointer (self->priv->result, + conn, g_object_unref); + } + + complete_operation (self); +} + +static void +check_tube_open (TpDBusTubeChannel *self) +{ + if (self->priv->result == NULL) + return; + + if (self->priv->address == NULL) + return; + + if (self->priv->state != TP_TUBE_CHANNEL_STATE_OPEN) + return; + + DEBUG ("Tube %s opened: %s", tp_proxy_get_object_path (self), + self->priv->address); + + g_dbus_connection_new_for_address (self->priv->address, + G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT, NULL, + NULL, dbus_connection_new_cb, self); +} + +static void +dbus_tube_invalidated_cb ( + TpProxy *proxy, + guint domain, + gint code, + gchar *message, + gpointer user_data) +{ + TpDBusTubeChannel *self = TP_DBUS_TUBE_CHANNEL (proxy); + TpDBusTubeChannelPrivate *priv = self->priv; + GError error = { domain, code, message }; + + if (priv->result != NULL) + { + DEBUG ("Tube invalidated: '%s'; failing pending offer/accept method call", + message); + g_simple_async_result_set_from_error (priv->result, &error); + complete_operation (self); + } +} + +static void +tube_state_changed_cb (TpChannel *channel, + TpTubeChannelState state, + gpointer user_data, + GObject *weak_object) +{ + TpDBusTubeChannel *self = (TpDBusTubeChannel *) channel; + + self->priv->state = state; + + check_tube_open (self); +} + +static void tp_dbus_tube_channel_constructed (GObject *obj) { TpDBusTubeChannel *self = (TpDBusTubeChannel *) obj; @@ -175,6 +314,85 @@ tp_dbus_tube_channel_constructed (GObject *obj) TP_HASH_TYPE_STRING_VARIANT_MAP, params); } } + + g_signal_connect (self, "invalidated", + G_CALLBACK (dbus_tube_invalidated_cb), NULL); +} + +static void +get_state_cb (TpProxy *proxy, + const GValue *value, + const GError *error, + gpointer user_data, + GObject *weak_object) +{ + TpDBusTubeChannel *self = (TpDBusTubeChannel *) proxy; + GSimpleAsyncResult *result = user_data; + + if (error != NULL) + { + DEBUG ("Failed to get Tube.State property: %s", error->message); + + g_simple_async_result_set_error (result, error->domain, error->code, + "Failed to get Tube.State property: %s", error->message); + } + else + { + self->priv->state = g_value_get_uint (value); + } + + g_simple_async_result_complete (result); +} + +static void +tp_dbus_tube_channel_prepare_core_feature_async (TpProxy *proxy, + const TpProxyFeature *feature, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *result; + GError *error = NULL; + TpChannel *chan = (TpChannel *) proxy; + + result = g_simple_async_result_new ((GObject *) proxy, callback, user_data, + tp_dbus_tube_channel_prepare_core_feature_async); + + if (tp_cli_channel_interface_tube_connect_to_tube_channel_state_changed (chan, + tube_state_changed_cb, proxy, NULL, NULL, &error) == NULL) + { + WARNING ("Failed to connect to TubeChannelStateChanged on %s: %s", + tp_proxy_get_object_path (proxy), error->message); + g_error_free (error); + } + + tp_cli_dbus_properties_call_get (proxy, -1, + TP_IFACE_CHANNEL_INTERFACE_TUBE, "State", + get_state_cb, result, g_object_unref, G_OBJECT (proxy)); +} + +enum { + FEAT_CORE, + N_FEAT +}; + +static const TpProxyFeature * +tp_dbus_tube_channel_list_features (TpProxyClass *cls G_GNUC_UNUSED) +{ + static TpProxyFeature features[N_FEAT + 1] = { { 0 } }; + + if (G_LIKELY (features[0].name != 0)) + return features; + + features[FEAT_CORE].name = + TP_DBUS_TUBE_CHANNEL_FEATURE_CORE; + features[FEAT_CORE].prepare_async = + tp_dbus_tube_channel_prepare_core_feature_async; + features[FEAT_CORE].core = TRUE; + + /* assert that the terminator at the end is there */ + g_assert (features[N_FEAT].name == 0); + + return features; } static void @@ -182,11 +400,14 @@ tp_dbus_tube_channel_class_init (TpDBusTubeChannelClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); GParamSpec *param_spec; + TpProxyClass *proxy_class = (TpProxyClass *) klass; gobject_class->constructed = tp_dbus_tube_channel_constructed; gobject_class->get_property = tp_dbus_tube_channel_get_property; gobject_class->dispose = tp_dbus_tube_channel_dispose; + proxy_class->list_features = tp_dbus_tube_channel_list_features; + /** * TpDBusTubeChannel:service-name: * @@ -290,3 +511,256 @@ tp_dbus_tube_channel_get_parameters (TpDBusTubeChannel *self) { return self->priv->parameters; } + +/** + * TP_DBUS_TUBE_CHANNEL_FEATURE_CORE: + * + * Expands to a call to a function that returns a quark representing the + * core feature of a #TpDBusTubeChannel. + * + * One can ask for a feature to be prepared using the + * tp_proxy_prepare_async() function, and waiting for it to callback. + * + * Since: 0.18.0 + */ +GQuark +tp_dbus_tube_channel_feature_quark_core (void) +{ + return g_quark_from_static_string ("tp-dbus-tube-channel-feature-core"); +} + +static void +dbus_tube_offer_cb (TpChannel *channel, + const gchar *address, + const GError *error, + gpointer user_data, + GObject *weak_object) +{ + TpDBusTubeChannel *self = (TpDBusTubeChannel *) channel; + + if (error != NULL) + { + DEBUG ("Offer() failed: %s", error->message); + + g_simple_async_result_set_from_error (self->priv->result, error); + complete_operation (self); + return; + } + + self->priv->address = g_strdup (address); + + /* We have to wait that the tube is opened before being allowed to use it */ + check_tube_open (self); +} + +static void +proxy_prepare_offer_cb (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + TpDBusTubeChannel *self = (TpDBusTubeChannel *) source; + GHashTable *params = user_data; + GError *error = NULL; + + if (!tp_proxy_prepare_finish (source, result, &error)) + { + g_simple_async_result_take_error (self->priv->result, error); + complete_operation (self); + goto out; + } + + if (self->priv->state != TP_TUBE_CHANNEL_STATE_NOT_OFFERED) + { + g_simple_async_result_set_error (self->priv->result, TP_ERRORS, + TP_ERROR_INVALID_ARGUMENT, "Tube is not in the NotOffered state"); + complete_operation (self); + goto out; + } + + g_assert (self->priv->parameters == NULL); + if (params != NULL) + self->priv->parameters = g_hash_table_ref (params); + else + self->priv->parameters = tp_asv_new (NULL, NULL); + + g_object_notify (G_OBJECT (self), "parameters"); + + /* TODO: provide a way to use TP_SOCKET_ACCESS_CONTROL_LOCALHOST if you're in + * an environment where you need to disable authentication. tp-glib can't + * guess this for you. + */ + tp_cli_channel_type_dbus_tube_call_offer (TP_CHANNEL (self), -1, + self->priv->parameters, TP_SOCKET_ACCESS_CONTROL_CREDENTIALS, + dbus_tube_offer_cb, NULL, NULL, G_OBJECT (self)); + +out: + tp_clear_pointer (¶ms, g_hash_table_unref); +} + +/** + * tp_dbus_tube_channel_offer_async + * @self: an outgoing #TpDBusTubeChannel + * @params: (allow-none) (transfer none): parameters of the tube, or %NULL + * @callback: a callback to call when the tube has been offered + * @user_data: data to pass to @callback + * + * Offer an outgoing D-Bus tube. When the tube has been offered and accepted + * @callback will be called. You can then call + * tp_dbus_tube_channel_offer_finish() to get the #GDBusConnection that will + * be used to communicate through the tube. + * + * Since: 0.18.0 + */ +void +tp_dbus_tube_channel_offer_async (TpDBusTubeChannel *self, + GHashTable *params, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GQuark features[] = { TP_DBUS_TUBE_CHANNEL_FEATURE_CORE, 0 }; + + g_return_if_fail (TP_IS_DBUS_TUBE_CHANNEL (self)); + g_return_if_fail (self->priv->result == NULL); + g_return_if_fail (tp_channel_get_requested (TP_CHANNEL (self))); + g_return_if_fail (self->priv->parameters == NULL); + + self->priv->result = g_simple_async_result_new (G_OBJECT (self), callback, + user_data, tp_dbus_tube_channel_offer_async); + + /* We need CORE to be prepared as we rely on State changes */ + tp_proxy_prepare_async (self, features, proxy_prepare_offer_cb, + params != NULL ? g_hash_table_ref (params) : params); +} + +/** + * tp_dbus_tube_channel_offer_finish: + * @self: a #TpDBusTubeChannel + * @result: a #GAsyncResult + * @error: a #GError to fill + * + * Finishes offering an outgoing D-Bus tube. The returned #GDBusConnection + * is ready to be used to exchange data through the tube. + * + * Returns: (transfer full): a reference on a #GDBusConnection if the tube + * has been successfully offered and opened; %NULL otherwise. + * + * Since: 0.18.0 + */ +GDBusConnection * +tp_dbus_tube_channel_offer_finish (TpDBusTubeChannel *self, + GAsyncResult *result, + GError **error) +{ + _tp_implement_finish_return_copy_pointer (self, + tp_dbus_tube_channel_offer_async, g_object_ref) +} + +static void +dbus_tube_accept_cb (TpChannel *channel, + const gchar *address, + const GError *error, + gpointer user_data, + GObject *weak_object) +{ + TpDBusTubeChannel *self = (TpDBusTubeChannel *) channel; + + if (error != NULL) + { + DEBUG ("Accept() failed: %s", error->message); + + g_simple_async_result_set_from_error (self->priv->result, error); + complete_operation (self); + return; + } + + self->priv->address = g_strdup (address); + + /* We have to wait that the tube is opened before being allowed to use it */ + check_tube_open (self); +} + +static void +proxy_prepare_accept_cb (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + TpDBusTubeChannel *self = (TpDBusTubeChannel *) source; + GError *error = NULL; + + if (!tp_proxy_prepare_finish (source, result, &error)) + { + g_simple_async_result_take_error (self->priv->result, error); + complete_operation (self); + return; + } + + if (self->priv->state != TP_TUBE_CHANNEL_STATE_LOCAL_PENDING) + { + g_simple_async_result_set_error (self->priv->result, TP_ERRORS, + TP_ERROR_INVALID_ARGUMENT, "Tube is not in the LocalPending state"); + complete_operation (self); + return; + } + + /* TODO: provide a way to use TP_SOCKET_ACCESS_CONTROL_LOCALHOST if you're in + * an environment where you need to disable authentication. tp-glib can't + * guess this for you. + */ + tp_cli_channel_type_dbus_tube_call_accept (TP_CHANNEL (self), -1, + TP_SOCKET_ACCESS_CONTROL_CREDENTIALS, dbus_tube_accept_cb, + NULL, NULL, G_OBJECT (self)); +} + +/** + * tp_dbus_tube_channel_accept_async + * @self: an incoming #TpDBusTubeChannel + * @callback: a callback to call when the tube has been offered + * @user_data: data to pass to @callback + * + * Accept an incoming D-Bus tube. When the tube has been accepted + * @callback will be called. You can then call + * tp_dbus_tube_channel_accept_finish() to get the #GDBusConnection that will + * be used to communicate through the tube. + * + * Since: 0.18.0 + */ +void +tp_dbus_tube_channel_accept_async (TpDBusTubeChannel *self, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GQuark features[] = { TP_DBUS_TUBE_CHANNEL_FEATURE_CORE, 0 }; + + g_return_if_fail (TP_IS_DBUS_TUBE_CHANNEL (self)); + g_return_if_fail (self->priv->result == NULL); + g_return_if_fail (!tp_channel_get_requested (TP_CHANNEL (self))); + + self->priv->result = g_simple_async_result_new (G_OBJECT (self), callback, + user_data, tp_dbus_tube_channel_accept_async); + + /* We need CORE to be prepared as we rely on State changes */ + tp_proxy_prepare_async (self, features, proxy_prepare_accept_cb, NULL); +} + +/** + * tp_dbus_tube_channel_accept_finish: + * @self: a #TpDBusTubeChannel + * @result: a #GAsyncResult + * @error: a #GError to fill + * + * Finishes to accept an incoming D-Bus tube. The returned #GDBusConnection + * is ready to be used to exchange data through the tube. + * + * Returns: (transfer full): a reference on a #GDBusConnection if the tube + * has been successfully accepted and opened; %NULL otherwise. + * + * Since: 0.18.0 + */ +GDBusConnection * +tp_dbus_tube_channel_accept_finish (TpDBusTubeChannel *self, + GAsyncResult *result, + GError **error) +{ + _tp_implement_finish_return_copy_pointer (self, + tp_dbus_tube_channel_accept_async, g_object_ref) +} diff --git a/telepathy-glib/dbus-tube-channel.h b/telepathy-glib/dbus-tube-channel.h index 260bdf066..28b81c1fc 100644 --- a/telepathy-glib/dbus-tube-channel.h +++ b/telepathy-glib/dbus-tube-channel.h @@ -50,12 +50,37 @@ struct _TpDBusTubeChannelClass GCallback _padding[7]; }; +#define TP_DBUS_TUBE_CHANNEL_FEATURE_CORE \ + tp_dbus_tube_channel_feature_quark_core () +GQuark tp_dbus_tube_channel_feature_quark_core (void) G_GNUC_CONST; + GType tp_dbus_tube_channel_get_type (void); const gchar * tp_dbus_tube_channel_get_service_name (TpDBusTubeChannel *self); GHashTable * tp_dbus_tube_channel_get_parameters (TpDBusTubeChannel *self); +/* Outgoing tube methods */ + +void tp_dbus_tube_channel_offer_async (TpDBusTubeChannel *self, + GHashTable *params, + GAsyncReadyCallback callback, + gpointer user_data); + +GDBusConnection * tp_dbus_tube_channel_offer_finish (TpDBusTubeChannel *self, + GAsyncResult *result, + GError **error) G_GNUC_WARN_UNUSED_RESULT; + +/* Incoming tube methods */ + +void tp_dbus_tube_channel_accept_async (TpDBusTubeChannel *self, + GAsyncReadyCallback callback, + gpointer user_data); + +GDBusConnection * tp_dbus_tube_channel_accept_finish (TpDBusTubeChannel *self, + GAsyncResult *result, + GError **error) G_GNUC_WARN_UNUSED_RESULT; + G_END_DECLS #endif diff --git a/telepathy-glib/introspection.am b/telepathy-glib/introspection.am index 9bc3bbac3..ee9f5386f 100644 --- a/telepathy-glib/introspection.am +++ b/telepathy-glib/introspection.am @@ -90,12 +90,19 @@ TelepathyGLib_1_gir_LIBS = \ $(NULL) TelepathyGLib_1_gir_EXPORT_PACKAGES = telepathy-glib-1 +if OFFICIAL_RELEASE +SCANNER_WARN_ERROR = +else +SCANNER_WARN_ERROR = --warn-error +endif + TelepathyGLib_1_gir_SCANNERFLAGS = \ --identifier-prefix=Tp \ -I$(top_builddir) \ -I$(top_srcdir) \ --c-include="telepathy-glib/telepathy-glib.h" \ --warn-all \ + $(SCANNER_WARN_ERROR) \ $(NULL) TelepathyGLib_1_gir_INCLUDES = \ @@ -120,6 +127,7 @@ TelepathyGLibDBus_1_gir_SCANNERFLAGS = \ -I$(top_srcdir) \ --c-include="telepathy-glib/telepathy-glib.h" \ --warn-all \ + $(SCANNER_WARN_ERROR) \ --include-uninstalled=TelepathyGLib-1.gir \ $(NULL) TelepathyGLibDBus_1_gir_INCLUDES = \ diff --git a/telepathy-glib/protocol.c b/telepathy-glib/protocol.c index 8d972322c..7e3d6603b 100644 --- a/telepathy-glib/protocol.c +++ b/telepathy-glib/protocol.c @@ -910,7 +910,7 @@ const TpConnectionManagerParam *tp_protocol_get_param (TpProtocol *self, * Returns: (transfer full): a structure representing the parameter @param, * or %NULL if not supported. Free with tp_connection_manager_param_free() * - * Since: 0.UNRELEASED + * Since: 0.17.6 */ TpConnectionManagerParam * tp_protocol_dup_param (TpProtocol *self, @@ -973,7 +973,7 @@ tp_protocol_dup_param_names (TpProtocol *self) * Returns: (transfer none): an array of #TpConnectionManagerParam structures, * terminated by one whose @name is %NULL * - * Since: 0.UNRELEASED + * Since: 0.17.6 */ const TpConnectionManagerParam * tp_protocol_borrow_params (TpProtocol *self) @@ -996,7 +996,7 @@ tp_protocol_borrow_params (TpProtocol *self) * Returns: (transfer full) (element-type TelepathyGLib.ConnectionManagerParam): * a list of #TpConnectionManagerParam structures, owned by the caller * - * Since: 0.UNRELEASED + * Since: 0.17.6 */ GList * tp_protocol_dup_params (TpProtocol *self) diff --git a/telepathy-glib/proxy.c b/telepathy-glib/proxy.c index 3782e3b4a..100b4ce09 100644 --- a/telepathy-glib/proxy.c +++ b/telepathy-glib/proxy.c @@ -553,7 +553,7 @@ static void tp_proxy_poll_features (TpProxy *self, const GError *error); static gboolean tp_proxy_emit_invalidated (gpointer p) { - TpProxy *self = p; + TpProxy *self = TP_PROXY (p); g_signal_emit (self, signals[SIGNAL_INVALIDATED], 0, self->invalidated->domain, self->invalidated->code, @@ -1395,7 +1395,7 @@ void _tp_proxy_ensure_factory (gpointer proxy, TpSimpleClientFactory *factory) { - TpProxy *self = proxy; + TpProxy *self = TP_PROXY (proxy); if (self->priv->factory != NULL) return; @@ -1801,7 +1801,7 @@ depends_prepare_cb (GObject *source, GAsyncResult *result, gpointer user_data) { - TpProxy *self = (TpProxy *) source; + TpProxy *self = TP_PROXY (source); tp_proxy_poll_features (self, NULL); } @@ -1991,7 +1991,7 @@ feature_prepared_cb (GObject *source, GAsyncResult *result, gpointer user_data) { - TpProxy *self = (TpProxy *) source; + TpProxy *self = TP_PROXY (source); TpProxyFeature *feature = user_data; GError *error = NULL; gboolean prepared = TRUE; @@ -2282,7 +2282,7 @@ prepare_before_signalling_connected_cb (GObject *source, GAsyncResult *result, gpointer user_data) { - TpProxy *self = user_data; + TpProxy *self = TP_PROXY (user_data); /* We don't care if the call succeeded or not as it was already prepared */ self->priv->pending_will_announce_calls--; diff --git a/telepathy-glib/stream-tube-channel.c b/telepathy-glib/stream-tube-channel.c index e36cea3f2..473fac0ce 100644 --- a/telepathy-glib/stream-tube-channel.c +++ b/telepathy-glib/stream-tube-channel.c @@ -481,9 +481,12 @@ tp_stream_tube_channel_init (TpStreamTubeChannel *self) * strings (D-Bus interface name + "." + property name) to #GValue instances * @error: used to indicate the error if %NULL is returned * - * Convenient function to create a new #TpStreamTubeChannel + * Creates a new #TpStreamTubeChannel proxy object from the provided path and + * properties. Most developers will not need to use this function; use + * #TpAutomaticProxyFactory to automatically create #TpStreamTubeChannel proxy + * objects. * - * Returns: (transfer full): a newly created #TpStreamTubeChannel + * Returns: (transfer full): a newly-created #TpStreamTubeChannel proxy * * Since: 0.13.2 */ @@ -920,7 +923,7 @@ tp_stream_tube_channel_accept_async (TpStreamTubeChannel *self, * @result: a #GAsyncResult * @error: a #GError to fill * - * Finishes to accept an incoming stream tube. The returned + * Finishes accepting an incoming stream tube. The returned * #TpStreamTubeConnection can then be used to exchange data through the tube. * * Returns: (transfer full): a newly created #TpStreamTubeConnection @@ -1494,7 +1497,7 @@ tp_stream_tube_channel_offer_async (TpStreamTubeChannel *self, * @result: a #GAsyncResult * @error: a #GError to fill * - * Finishes to offer an outgoing stream tube. + * Finishes offering an outgoing stream tube. * * Returns: %TRUE when a Tube has been successfully offered; %FALSE otherwise * diff --git a/telepathy-glib/text-channel.c b/telepathy-glib/text-channel.c index 7d61daa1e..b2caf08fe 100644 --- a/telepathy-glib/text-channel.c +++ b/telepathy-glib/text-channel.c @@ -1238,16 +1238,17 @@ tp_text_channel_send_message_async (TpTextChannel *self, /** * tp_text_channel_send_message_finish: * @self: a #TpTextChannel - * @result: a #GAsyncResult + * @result: a #GAsyncResult passed to the callback for tp_text_channel_send_message_async() * @token: (out) (transfer full): if not %NULL, used to return the * token of the sent message * @error: a #GError to fill * - * Finishes to send a message. + * Completes a call to tp_text_channel_send_message_async(). * * @token can be used to match any incoming delivery or failure reports - * against the sent message. If the returned token is %NULL the - * message is not readily identifiable. + * against the sent message. If this function returns true but the returned + * token is %NULL, the message was sent successfully but the protocol does not + * provide a way to identify it later. * * Returns: %TRUE if the message has been submitted to the server, %FALSE * otherwise. @@ -1379,10 +1380,10 @@ tp_text_channel_ack_messages_async (TpTextChannel *self, /** * tp_text_channel_ack_messages_finish: * @self: a #TpTextChannel - * @result: a #GAsyncResult + * @result: a #GAsyncResult passed to the callback for tp_text_channel_ack_messages_async() * @error: a #GError to fill * - * Finishes to ack a list of messages. + * Finishes acknowledging a list of messages. * * Returns: %TRUE if the messages have been acked, %FALSE otherwise. * @@ -1460,10 +1461,10 @@ tp_text_channel_ack_message_async (TpTextChannel *self, /** * tp_text_channel_ack_message_finish: * @self: a #TpTextChannel - * @result: a #GAsyncResult + * @result: a #GAsyncResult passed to the callback for tp_text_channel_ack_message_async() * @error: a #GError to fill * - * Finishes to ack a message. + * Finishes acknowledging a message. * * Returns: %TRUE if the message has been acked, %FALSE otherwise. * @@ -1528,10 +1529,10 @@ tp_text_channel_set_chat_state_async (TpTextChannel *self, /** * tp_text_channel_set_chat_state_finish: * @self: a #TpTextChannel - * @result: a #GAsyncResult + * @result: a #GAsyncResult passed to the callback for tp_text_channel_set_chat_state_async() * @error: a #GError to fill * - * Finishes to set chat state. + * Completes a call to tp_text_channel_set_chat_state_async(). * * Returns: %TRUE if the chat state has been changed, %FALSE otherwise. * diff --git a/telepathy-glib/versions/0.17.6.abi b/telepathy-glib/versions/0.17.6.abi new file mode 100644 index 000000000..34f882683 --- /dev/null +++ b/telepathy-glib/versions/0.17.6.abi @@ -0,0 +1,32 @@ +Version: TELEPATHY_GLIB_0.17.6 +Extends: TELEPATHY_GLIB_0.17.5 +Release: 0.17.6 + +tp_account_dup_detailed_error_vardict +tp_account_dup_parameters_vardict +tp_account_dup_storage_identifier_variant +tp_account_dup_storage_specific_information_vardict_async +tp_account_dup_storage_specific_information_vardict_finish +tp_account_update_parameters_vardict_async +tp_account_update_parameters_vardict_finish +tp_base_media_call_channel_get_local_hold_state +tp_call_channel_has_hold +tp_call_channel_request_hold_async +tp_call_channel_request_hold_finish +tp_call_content_media_description_add_rtcp_extended_reports_interface +tp_call_content_media_description_add_rtcp_feedback_interface +tp_call_content_media_description_add_rtcp_feedback_message +tp_call_content_media_description_add_rtp_header_extension +tp_call_content_media_description_add_rtp_header_extensions_interface +tp_call_content_media_description_set_does_avpf +tp_call_content_media_description_set_rtcp_extended_reports +tp_call_content_media_description_set_rtcp_feedback_minimum_interval +tp_capabilities_supports_audio_call +tp_capabilities_supports_audio_video_call +tp_capabilities_supports_file_transfer +tp_connection_manager_dup_protocols +tp_list_connection_managers_async +tp_list_connection_managers_finish +tp_protocol_borrow_params +tp_protocol_dup_param +tp_protocol_dup_params diff --git a/telepathy-glib/versions/0.17.7.abi b/telepathy-glib/versions/0.17.7.abi new file mode 100644 index 000000000..e8becd19f --- /dev/null +++ b/telepathy-glib/versions/0.17.7.abi @@ -0,0 +1,5 @@ +Version: TELEPATHY_GLIB_0.17.7 +Extends: TELEPATHY_GLIB_0.17.6 +Release: 0.17.7 + +tp_base_media_call_stream_get_local_sending diff --git a/telepathy-glib/versions/0.18.0.abi b/telepathy-glib/versions/0.18.0.abi new file mode 100644 index 000000000..0ade1606f --- /dev/null +++ b/telepathy-glib/versions/0.18.0.abi @@ -0,0 +1,12 @@ +Version: TELEPATHY_GLIB_0.18.0 +Extends: TELEPATHY_GLIB_0.17.7 +Release: 0.18.0 + +tp_dbus_tube_channel_accept_async +tp_dbus_tube_channel_accept_finish +tp_dbus_tube_channel_offer_async +tp_dbus_tube_channel_offer_finish +tp_dbus_tube_channel_feature_quark_core +tp_base_contact_list_get_download_at_connection +tp_base_contact_list_download_async +tp_base_contact_list_download_finish
\ No newline at end of file diff --git a/tests/dbus/account-manager.c b/tests/dbus/account-manager.c index 4cf1cb5c9..40faf7ff6 100644 --- a/tests/dbus/account-manager.c +++ b/tests/dbus/account-manager.c @@ -574,18 +574,13 @@ test_prepare_most_available (Test *test, gconstpointer data, guint nb_accounts) { - GPtrArray *accounts; - - accounts = g_ptr_array_new_with_free_func (g_free); - if (nb_accounts >= 1) - g_ptr_array_add (accounts, g_strdup (ACCOUNT1_PATH)); + tp_tests_simple_account_manager_add_account (test->service, ACCOUNT1_PATH, + TRUE); if (nb_accounts >= 2) - g_ptr_array_add (accounts, g_strdup (ACCOUNT2_PATH)); - - tp_tests_simple_account_manager_set_usable_accounts (test->service, accounts); - g_ptr_array_unref (accounts); + tp_tests_simple_account_manager_add_account (test->service, ACCOUNT2_PATH, + TRUE); test_prepare (test, data); script_append_action (test, manager_new_action, NULL); diff --git a/tests/dbus/contact-lists.c b/tests/dbus/contact-lists.c index d418c1e43..8aba03cb1 100644 --- a/tests/dbus/contact-lists.c +++ b/tests/dbus/contact-lists.c @@ -1883,6 +1883,25 @@ test_unblock_contacts_no_op (Test *test, unblock_contacts_no_op (test, call_unblock_contacts); } +static void +download_contacts_cb ( + TpConnection *conn, + const GError *error, gpointer user_data, + GObject *weak_object) +{ + g_assert_error (error, TP_ERRORS, TP_ERROR_NOT_IMPLEMENTED); +} + +static void +test_download_contacts (Test *test, + gconstpointer nil G_GNUC_UNUSED) +{ + tp_cli_connection_interface_contact_list_call_download ( + test->conn, -1, download_contacts_cb, test, test_quit_loop, NULL); + + g_main_loop_run (test->main_loop); +} + int main (int argc, char **argv) @@ -1994,5 +2013,8 @@ main (int argc, g_test_add ("/contact-lists/unblock-contacts/no-op", Test, NULL, setup, test_unblock_contacts_no_op, teardown); + g_test_add ("/contact-lists/download", + Test, NULL, setup, test_download_contacts, teardown); + return g_test_run (); } diff --git a/tests/dbus/contacts.c b/tests/dbus/contacts.c index 9eb7199fd..5fd37b3bc 100644 --- a/tests/dbus/contacts.c +++ b/tests/dbus/contacts.c @@ -215,24 +215,41 @@ contact_info_prepare_cb (GObject *object, if (tp_proxy_prepare_finish (connection, res, &result->error)) { TpContactInfoFlags flags; - GList *specs; - TpContactInfoFieldSpec *spec; + GList *specs, *l; flags = tp_connection_get_contact_info_flags (connection); g_assert_cmpint (flags, ==, TP_CONTACT_INFO_FLAG_PUSH | TP_CONTACT_INFO_FLAG_CAN_SET); specs = tp_connection_get_contact_info_supported_fields (connection); - g_assert (specs != NULL); - g_assert (specs->data != NULL); - g_assert (specs->next == NULL); + g_assert_cmpuint (g_list_length (specs), ==, 5); - spec = specs->data; - g_assert_cmpstr (spec->name, ==, "n"); - g_assert (spec->parameters != NULL); - g_assert (spec->parameters[0] == NULL); - g_assert_cmpint (spec->flags, ==, 0); - g_assert_cmpint (spec->max, ==, 0); + for (l = specs; l != NULL; l = l->next) + { + TpContactInfoFieldSpec *spec = l->data; + + if (!tp_strdiff (spec->name, "bday") || + !tp_strdiff (spec->name, "fn")) + { + g_assert (spec->parameters != NULL); + g_assert (spec->parameters[0] == NULL); + g_assert_cmpint (spec->flags, ==, 0); + g_assert_cmpint (spec->max, ==, 1); + } + else if (!tp_strdiff (spec->name, "email") || + !tp_strdiff (spec->name, "tel") || + !tp_strdiff (spec->name, "url")) + { + g_assert (spec->parameters != NULL); + g_assert (spec->parameters[0] == NULL); + g_assert_cmpint (spec->flags, ==, 0); + g_assert_cmpint (spec->max, ==, G_MAXUINT32); + } + else + { + g_assert_not_reached (); + } + } g_list_free (specs); } @@ -642,10 +659,7 @@ test_avatar_data (Fixture *f, { TpTestsContactsConnection *service_conn = f->service_conn; TpConnection *client_conn = f->client_conn; - static const gchar letters[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; - gchar rand_str[RAND_STR_LEN + 1]; gchar *dir; - guint i; gboolean avatar_retrieved_called; GError *error = NULL; GFile *file1, *file2; @@ -654,14 +668,9 @@ test_avatar_data (Fixture *f, g_message (G_STRFUNC); /* Make sure g_get_user_cache_dir() returns a tmp directory, to not mess up - * user's cache dir. - * FIXME: Replace this with g_mkdtemp once it gets added to GLib. - * See GNOME bug #118563 */ - for (i = 0; i < RAND_STR_LEN; i++) - rand_str[i] = letters[g_random_int_range (0, strlen (letters))]; - rand_str[RAND_STR_LEN] = '\0'; - dir = g_build_filename (g_get_tmp_dir (), rand_str, NULL); - g_assert (g_mkdir (dir, 0700) == 0); + * user's cache dir. */ + dir = g_dir_make_tmp ("tp-glib-tests-XXXXXX", &error); + g_assert_no_error (error); g_setenv ("XDG_CACHE_HOME", dir, TRUE); g_assert_cmpstr (g_get_user_cache_dir (), ==, dir); @@ -2083,7 +2092,7 @@ test_subscription_states (Fixture *f, { TpHandle alice_handle; TpContact *alice; - TestContactListManager *manager; + TpTestsContactListManager *manager; TpContactFeature features[] = { TP_CONTACT_FEATURE_SUBSCRIPTION_STATES }; SubscriptionStates states = { TP_SUBSCRIPTION_STATE_NO, TP_SUBSCRIPTION_STATE_NO, "", f->result.loop }; @@ -2116,13 +2125,13 @@ test_subscription_states (Fixture *f, G_CALLBACK (subscription_states_changed_cb), &states); /* Request subscription */ - test_contact_list_manager_request_subscription (manager, 1, &alice_handle, ""); + tp_tests_contact_list_manager_request_subscription (manager, 1, &alice_handle, ""); states.subscribe = TP_SUBSCRIPTION_STATE_ASK; g_main_loop_run (states.loop); /* Request again must re-emit the signal. Saying please this time will make * the request accepted and will ask for publish. */ - test_contact_list_manager_request_subscription (manager, 1, &alice_handle, "please"); + tp_tests_contact_list_manager_request_subscription (manager, 1, &alice_handle, "please"); g_main_loop_run (states.loop); states.subscribe = TP_SUBSCRIPTION_STATE_YES; states.publish = TP_SUBSCRIPTION_STATE_ASK; @@ -2130,7 +2139,7 @@ test_subscription_states (Fixture *f, g_main_loop_run (states.loop); /* Remove the contact */ - test_contact_list_manager_remove (manager, 1, &alice_handle); + tp_tests_contact_list_manager_remove (manager, 1, &alice_handle); states.subscribe = TP_SUBSCRIPTION_STATE_NO; states.publish = TP_SUBSCRIPTION_STATE_NO; states.publish_request = ""; @@ -2175,7 +2184,7 @@ test_contact_groups (Fixture *f, { TpHandle alice_handle; TpContact *alice; - TestContactListManager *manager; + TpTestsContactListManager *manager; TpContactFeature features[] = { TP_CONTACT_FEATURE_CONTACT_GROUPS }; ContactGroups data; @@ -2210,15 +2219,15 @@ test_contact_groups (Fixture *f, G_CALLBACK (contact_groups_changed_cb), &data); g_ptr_array_add (data.groups, "group1"); - test_contact_list_manager_add_to_group (manager, "group1", alice_handle); + tp_tests_contact_list_manager_add_to_group (manager, "group1", alice_handle); g_main_loop_run (data.loop); g_ptr_array_add (data.groups, "group2"); - test_contact_list_manager_add_to_group (manager, "group2", alice_handle); + tp_tests_contact_list_manager_add_to_group (manager, "group2", alice_handle); g_main_loop_run (data.loop); g_ptr_array_remove_index_fast (data.groups, 0); - test_contact_list_manager_remove_from_group (manager, "group1", alice_handle); + tp_tests_contact_list_manager_remove_from_group (manager, "group1", alice_handle); g_main_loop_run (data.loop); g_ptr_array_set_size (data.groups, 0); @@ -2489,7 +2498,7 @@ test_contact_list (Fixture *f, { const GQuark conn_features[] = { TP_CONNECTION_FEATURE_CONTACT_LIST, 0 }; Result result = { g_main_loop_new (NULL, FALSE), NULL, NULL, NULL }; - TestContactListManager *manager; + TpTestsContactListManager *manager; TpSimpleClientFactory *factory; const gchar *id = "contact-list-id"; const gchar *alias = "Contact List Alias"; @@ -2507,14 +2516,14 @@ test_contact_list (Fixture *f, g_assert_cmpint (tp_connection_get_contact_list_state (f->client_conn), ==, TP_CONTACT_LIST_STATE_NONE); g_assert (tp_connection_get_contact_list_persists (f->client_conn)); - g_assert (!tp_connection_get_can_change_contact_list (f->client_conn)); - g_assert (!tp_connection_get_request_uses_message (f->client_conn)); + g_assert (tp_connection_get_can_change_contact_list (f->client_conn)); + g_assert (tp_connection_get_request_uses_message (f->client_conn)); /* Add a remote-pending contact in our roster CM-side */ handle = tp_handle_ensure (f->service_repo, id, NULL, NULL); tp_tests_contacts_connection_change_aliases (f->service_conn, 1, &handle, &alias); - test_contact_list_manager_request_subscription (manager, 1, &handle, message); + tp_tests_contact_list_manager_request_subscription (manager, 1, &handle, message); /* Tell connection's factory contact features we want */ factory = tp_proxy_get_factory (f->client_conn); diff --git a/tests/dbus/dbus-tube.c b/tests/dbus/dbus-tube.c index cf5ad7720..d9492c654 100644 --- a/tests/dbus/dbus-tube.c +++ b/tests/dbus/dbus-tube.c @@ -34,6 +34,10 @@ typedef struct { TpConnection *connection; TpDBusTubeChannel *tube; + GDBusConnection *tube_conn; + GDBusConnection *cm_conn; + GVariant *call_result; + GError *error /* initialized where needed */; gint wait; } Test; @@ -68,6 +72,10 @@ teardown (Test *test, tp_tests_connection_assert_disconnect_succeeds (test->connection); g_object_unref (test->connection); g_object_unref (test->base_connection); + + g_clear_object (&test->tube_conn); + g_clear_object (&test->cm_conn); + tp_clear_pointer (&test->call_result, g_variant_unref); } static void @@ -206,6 +214,249 @@ test_properties (Test *test, g_hash_table_unref (parameters); } +static void +tube_offer_cb (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + Test *test = user_data; + + g_clear_object (&test->tube_conn); + + test->tube_conn = tp_dbus_tube_channel_offer_finish ( + TP_DBUS_TUBE_CHANNEL (source), result, &test->error); + + test->wait--; + if (test->wait <= 0) + g_main_loop_quit (test->mainloop); +} + +static gboolean +new_connection_cb (TpTestsDBusTubeChannel *chan, + GDBusConnection *connection, + Test *test) +{ + g_clear_object (&test->cm_conn); + test->cm_conn = g_object_ref (connection); + + test->wait--; + if (test->wait <= 0) + g_main_loop_quit (test->mainloop); + + return TRUE; +} + +static void +handle_double_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) +{ + if (!tp_strdiff (method_name, "Double")) + { + guint value; + + g_variant_get (parameters, "(i)", &value); + + g_dbus_method_invocation_return_value (invocation, + g_variant_new ("(i)", value * 2)); + } +} + +static void +register_object (GDBusConnection *connection) +{ + GDBusNodeInfo *introspection_data; + guint registration_id; + static const GDBusInterfaceVTable interface_vtable = + { + handle_double_call, + NULL, + NULL, + }; + static const gchar introspection_xml[] = + "<node>" + " <interface name='org.Example.TestInterface'>" + " <method name='Double'>" + " <arg type='i' name='value' direction='in'/>" + " <arg type='i' name='result' direction='out'/>" + " </method>" + " </interface>" + "</node>"; + + introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL); + g_assert (introspection_data != NULL); + + registration_id = g_dbus_connection_register_object (connection, + "/org/Example/TestObject", introspection_data->interfaces[0], + &interface_vtable, NULL, NULL, NULL); + g_assert (registration_id > 0); + + g_dbus_node_info_unref (introspection_data); +} + +static void +double_call_cb (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + Test *test = user_data; + + tp_clear_pointer (&test->call_result, g_variant_unref); + + test->call_result = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), + result, &test->error); + + test->wait--; + if (test->wait <= 0) + g_main_loop_quit (test->mainloop); +} + +static void +use_tube (Test *test, + GDBusConnection *server_conn, + GDBusConnection *client_conn) +{ + gint result; + + /* Server publishes an object on the tube */ + register_object (server_conn); + + /* Client calls a remote method */ + g_dbus_connection_call (client_conn, NULL, "/org/Example/TestObject", + "org.Example.TestInterface", "Double", + g_variant_new ("(i)", 42), + G_VARIANT_TYPE ("(i)"), G_DBUS_CALL_FLAGS_NONE, -1, + NULL, double_call_cb, test); + + test->wait = 1; + g_main_loop_run (test->mainloop); + g_assert_no_error (test->error); + + g_variant_get (test->call_result, "(i)", &result); + g_assert_cmpuint (result, ==, 42 * 2); +} + +static void +test_offer (Test *test, + gconstpointer data) +{ + const TpTestsDBusTubeChannelOpenMode open_mode = GPOINTER_TO_UINT (data); + GHashTable *params; + + /* Outgoing tube */ + create_tube_service (test, TRUE, TRUE); + tp_tests_dbus_tube_channel_set_open_mode (test->tube_chan_service, open_mode); + + params = tp_asv_new ("badger", G_TYPE_UINT, 42, NULL); + + g_signal_connect (test->tube_chan_service, "new-connection", + G_CALLBACK (new_connection_cb), test); + + tp_dbus_tube_channel_offer_async (test->tube, params, tube_offer_cb, test); + + test->wait = 2; + g_main_loop_run (test->mainloop); + g_assert_no_error (test->error); + + check_parameters (tp_dbus_tube_channel_get_parameters (test->tube)); + + g_assert (G_IS_DBUS_CONNECTION (test->tube_conn)); + g_assert (G_IS_DBUS_CONNECTION (test->cm_conn)); + + use_tube (test, test->tube_conn, test->cm_conn); +} + +static void +test_offer_invalidated_before_open (Test *test, + gconstpointer data G_GNUC_UNUSED) +{ + /* Outgoing tube */ + create_tube_service (test, TRUE, TRUE); + tp_tests_dbus_tube_channel_set_open_mode (test->tube_chan_service, + TP_TESTS_DBUS_TUBE_CHANNEL_NEVER_OPEN); + + tp_dbus_tube_channel_offer_async (test->tube, NULL, tube_offer_cb, test); + + test->wait = 1; + g_main_loop_run (test->mainloop); + /* FIXME: this isn't a particularly good error… it's just what comes out when + * the channel gets closed from under us, and there isn't really API on + * DBusTube to give a better error. + * + * https://bugs.freedesktop.org/show_bug.cgi?id=48196 + */ + g_assert_error (test->error, TP_DBUS_ERRORS, TP_DBUS_ERROR_OBJECT_REMOVED); +} + +static void +tube_accept_cb (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + Test *test = user_data; + + g_clear_object (&test->tube_conn); + + test->tube_conn = tp_dbus_tube_channel_accept_finish ( + TP_DBUS_TUBE_CHANNEL (source), result, &test->error); + + test->wait--; + if (test->wait <= 0) + g_main_loop_quit (test->mainloop); +} + +static void +test_accept (Test *test, + gconstpointer data) +{ + const TpTestsDBusTubeChannelOpenMode open_mode = GPOINTER_TO_UINT (data); + + /* Incoming tube */ + create_tube_service (test, FALSE, TRUE); + tp_tests_dbus_tube_channel_set_open_mode (test->tube_chan_service, open_mode); + + g_signal_connect (test->tube_chan_service, "new-connection", + G_CALLBACK (new_connection_cb), test); + + tp_dbus_tube_channel_accept_async (test->tube, tube_accept_cb, test); + + test->wait = 2; + g_main_loop_run (test->mainloop); + g_assert_no_error (test->error); + + g_assert (G_IS_DBUS_CONNECTION (test->tube_conn)); + g_assert (G_IS_DBUS_CONNECTION (test->cm_conn)); + + use_tube (test, test->cm_conn, test->tube_conn); +} + +static void +test_accept_invalidated_before_open (Test *test, + gconstpointer data G_GNUC_UNUSED) +{ + /* Incoming tube */ + create_tube_service (test, FALSE, TRUE); + tp_tests_dbus_tube_channel_set_open_mode (test->tube_chan_service, + TP_TESTS_DBUS_TUBE_CHANNEL_NEVER_OPEN); + + tp_dbus_tube_channel_accept_async (test->tube, tube_accept_cb, test); + + test->wait = 1; + g_main_loop_run (test->mainloop); + /* FIXME: this isn't a particularly good error… it's just what comes out when + * the channel gets closed from under us, and there isn't really API on + * DBusTube to give a better error. + * + * https://bugs.freedesktop.org/show_bug.cgi?id=48196 + */ + g_assert_error (test->error, TP_DBUS_ERRORS, TP_DBUS_ERROR_OBJECT_REMOVED); +} + int main (int argc, char **argv) @@ -217,6 +468,23 @@ main (int argc, teardown); g_test_add ("/dbus-tube/properties", Test, NULL, setup, test_properties, teardown); + /* Han shot first. */ + g_test_add ("/dbus-tube/offer-open-first", Test, + GUINT_TO_POINTER (TP_TESTS_DBUS_TUBE_CHANNEL_OPEN_FIRST), + setup, test_offer, teardown); + g_test_add ("/dbus-tube/offer-open-second", Test, + GUINT_TO_POINTER (TP_TESTS_DBUS_TUBE_CHANNEL_OPEN_SECOND), + setup, test_offer, teardown); + g_test_add ("/dbus-tube/offer-invalidated-before-open", Test, NULL, + setup, test_offer_invalidated_before_open, teardown); + g_test_add ("/dbus-tube/accept-open-first", Test, + GUINT_TO_POINTER (TP_TESTS_DBUS_TUBE_CHANNEL_OPEN_FIRST), + setup, test_accept, teardown); + g_test_add ("/dbus-tube/accept-open-second", Test, + GUINT_TO_POINTER (TP_TESTS_DBUS_TUBE_CHANNEL_OPEN_SECOND), + setup, test_accept, teardown); + g_test_add ("/dbus-tube/accept-invalidated-before-open", Test, NULL, + setup, test_accept_invalidated_before_open, teardown); return g_test_run (); } diff --git a/tests/lib/contact-list-manager.c b/tests/lib/contact-list-manager.c index a3ba21ed5..b3ad79de3 100644 --- a/tests/lib/contact-list-manager.c +++ b/tests/lib/contact-list-manager.c @@ -16,7 +16,7 @@ #include <string.h> #include <telepathy-glib/telepathy-glib.h> -struct _TestContactListManagerPrivate +struct _TpTestsContactListManagerPrivate { TpBaseConnection *conn; @@ -32,13 +32,17 @@ struct _TestContactListManagerPrivate static void contact_groups_iface_init (TpContactGroupListInterface *iface); static void mutable_contact_groups_iface_init ( TpMutableContactGroupListInterface *iface); +static void mutable_iface_init ( + TpMutableContactListInterface *iface); -G_DEFINE_TYPE_WITH_CODE (TestContactListManager, test_contact_list_manager, +G_DEFINE_TYPE_WITH_CODE (TpTestsContactListManager, tp_tests_contact_list_manager, TP_TYPE_BASE_CONTACT_LIST, G_IMPLEMENT_INTERFACE (TP_TYPE_CONTACT_GROUP_LIST, contact_groups_iface_init); G_IMPLEMENT_INTERFACE (TP_TYPE_MUTABLE_CONTACT_GROUP_LIST, - mutable_contact_groups_iface_init)) + mutable_contact_groups_iface_init) + G_IMPLEMENT_INTERFACE (TP_TYPE_MUTABLE_CONTACT_LIST, + mutable_iface_init)) typedef struct { TpSubscriptionState subscribe; @@ -63,7 +67,7 @@ contact_detail_destroy (gpointer p) } static ContactDetails * -lookup_contact (TestContactListManager *self, +lookup_contact (TpTestsContactListManager *self, TpHandle handle) { return g_hash_table_lookup (self->priv->contact_details, @@ -71,7 +75,7 @@ lookup_contact (TestContactListManager *self, } static ContactDetails * -ensure_contact (TestContactListManager *self, +ensure_contact (TpTestsContactListManager *self, TpHandle handle) { ContactDetails *d = lookup_contact (self, handle); @@ -95,17 +99,17 @@ ensure_contact (TestContactListManager *self, } static void -test_contact_list_manager_init (TestContactListManager *self) +tp_tests_contact_list_manager_init (TpTestsContactListManager *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, - TEST_TYPE_CONTACT_LIST_MANAGER, TestContactListManagerPrivate); + TP_TESTS_TYPE_CONTACT_LIST_MANAGER, TpTestsContactListManagerPrivate); self->priv->contact_details = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, contact_detail_destroy); } static void -close_all (TestContactListManager *self) +close_all (TpTestsContactListManager *self) { if (self->priv->status_changed_id != 0) { @@ -120,18 +124,18 @@ close_all (TestContactListManager *self) static void dispose (GObject *object) { - TestContactListManager *self = TEST_CONTACT_LIST_MANAGER (object); + TpTestsContactListManager *self = TP_TESTS_CONTACT_LIST_MANAGER (object); close_all (self); - ((GObjectClass *) test_contact_list_manager_parent_class)->dispose ( + ((GObjectClass *) tp_tests_contact_list_manager_parent_class)->dispose ( object); } static TpHandleSet * contact_list_dup_contacts (TpBaseContactList *base) { - TestContactListManager *self = TEST_CONTACT_LIST_MANAGER (base); + TpTestsContactListManager *self = TP_TESTS_CONTACT_LIST_MANAGER (base); TpHandleSet *set; GHashTableIter iter; gpointer k, v; @@ -159,7 +163,7 @@ contact_list_dup_states (TpBaseContactList *base, TpSubscriptionState *publish, gchar **publish_request) { - TestContactListManager *self = TEST_CONTACT_LIST_MANAGER (base); + TpTestsContactListManager *self = TP_TESTS_CONTACT_LIST_MANAGER (base); ContactDetails *d = lookup_contact (self, contact); if (d == NULL) @@ -189,7 +193,7 @@ contact_list_dup_states (TpBaseContactList *base, static GStrv contact_list_dup_groups (TpBaseContactList *base) { - TestContactListManager *self = TEST_CONTACT_LIST_MANAGER (base); + TpTestsContactListManager *self = TP_TESTS_CONTACT_LIST_MANAGER (base); GPtrArray *ret; if (self->priv->groups != NULL) @@ -219,7 +223,7 @@ static GStrv contact_list_dup_contact_groups (TpBaseContactList *base, TpHandle contact) { - TestContactListManager *self = TEST_CONTACT_LIST_MANAGER (base); + TpTestsContactListManager *self = TP_TESTS_CONTACT_LIST_MANAGER (base); ContactDetails *d = lookup_contact (self, contact); GPtrArray *ret; @@ -250,7 +254,7 @@ static TpHandleSet * contact_list_dup_group_members (TpBaseContactList *base, const gchar *group) { - TestContactListManager *self = TEST_CONTACT_LIST_MANAGER (base); + TpTestsContactListManager *self = TP_TESTS_CONTACT_LIST_MANAGER (base); GHashTableIter iter; gpointer k, v; TpHandleSet *set; @@ -302,18 +306,35 @@ contact_list_set_contact_groups_async (TpBaseContactList *base, GAsyncReadyCallback callback, gpointer user_data) { - TestContactListManager *self = TEST_CONTACT_LIST_MANAGER (base); + TpTestsContactListManager *self = TP_TESTS_CONTACT_LIST_MANAGER (base); ContactDetails *d; GHashTable *tmp; GPtrArray *added, *removed; + GPtrArray *new_groups; guint i; d = ensure_contact (self, contact); + new_groups = g_ptr_array_new (); /* make a hash table so we only have one difference function */ tmp = g_hash_table_new (g_str_hash, g_str_equal); for (i = 0; i < n; i++) - g_hash_table_insert (tmp, (gpointer) names[i], GUINT_TO_POINTER (1)); + { + g_hash_table_insert (tmp, (gpointer) names[i], GUINT_TO_POINTER (1)); + + if (g_hash_table_lookup (self->priv->groups, names[i]) == NULL) + { + g_hash_table_insert (self->priv->groups, g_strdup (names[i]), + GUINT_TO_POINTER (1)); + g_ptr_array_add (new_groups, (gchar *) names[i]); + } + } + + if (new_groups->len > 0) + { + tp_base_contact_list_groups_created ((TpBaseContactList *) self, + (const gchar * const *) new_groups->pdata, new_groups->len); + } /* see which groups were added and which were removed */ added = group_difference (tmp, d->groups); @@ -327,15 +348,19 @@ contact_list_set_contact_groups_async (TpBaseContactList *base, g_hash_table_insert (d->groups, g_strdup (names[i]), GUINT_TO_POINTER (1)); /* signal the change */ - tp_base_contact_list_one_contact_groups_changed (base, contact, - (const gchar * const *) added->pdata, added->len, - (const gchar * const *) removed->pdata, removed->len); + if (added->len > 0 || removed->len > 0) + { + tp_base_contact_list_one_contact_groups_changed (base, contact, + (const gchar * const *) added->pdata, added->len, + (const gchar * const *) removed->pdata, removed->len); + } tp_simple_async_report_success_in_idle ((GObject *) self, callback, user_data, contact_list_set_contact_groups_async); g_ptr_array_unref (added); g_ptr_array_unref (removed); + g_ptr_array_unref (new_groups); } static void @@ -398,10 +423,101 @@ contact_list_remove_group_async (TpBaseContactList *base, } static void +contact_list_request_subscription_async (TpBaseContactList *self, + TpHandleSet *contacts, + const gchar *message, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GArray *handles; + + handles = tp_handle_set_to_array (contacts); + tp_tests_contact_list_manager_request_subscription ( + (TpTestsContactListManager *) self, + handles->len, (TpHandle *) handles->data, message); + g_array_unref (handles); + + tp_simple_async_report_success_in_idle ((GObject *) self, callback, + user_data, contact_list_request_subscription_async); +} + +static void +contact_list_authorize_publication_async (TpBaseContactList *self, + TpHandleSet *contacts, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GArray *handles; + + handles = tp_handle_set_to_array (contacts); + tp_tests_contact_list_manager_authorize_publication ( + (TpTestsContactListManager *) self, + handles->len, (TpHandle *) handles->data); + g_array_unref (handles); + + tp_simple_async_report_success_in_idle ((GObject *) self, callback, + user_data, contact_list_authorize_publication_async); +} + +static void +contact_list_remove_contacts_async (TpBaseContactList *self, + TpHandleSet *contacts, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GArray *handles; + + handles = tp_handle_set_to_array (contacts); + tp_tests_contact_list_manager_remove ( + (TpTestsContactListManager *) self, + handles->len, (TpHandle *) handles->data); + g_array_unref (handles); + + tp_simple_async_report_success_in_idle ((GObject *) self, callback, + user_data, contact_list_remove_contacts_async); +} + +static void +contact_list_unsubscribe_async (TpBaseContactList *self, + TpHandleSet *contacts, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GArray *handles; + + handles = tp_handle_set_to_array (contacts); + tp_tests_contact_list_manager_unsubscribe ( + (TpTestsContactListManager *) self, + handles->len, (TpHandle *) handles->data); + g_array_unref (handles); + + tp_simple_async_report_success_in_idle ((GObject *) self, callback, + user_data, contact_list_unsubscribe_async); +} + +static void +contact_list_unpublish_async (TpBaseContactList *self, + TpHandleSet *contacts, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GArray *handles; + + handles = tp_handle_set_to_array (contacts); + tp_tests_contact_list_manager_unpublish ( + (TpTestsContactListManager *) self, + handles->len, (TpHandle *) handles->data); + g_array_unref (handles); + + tp_simple_async_report_success_in_idle ((GObject *) self, callback, + user_data, contact_list_unpublish_async); +} + +static void status_changed_cb (TpBaseConnection *conn, guint status, guint reason, - TestContactListManager *self) + TpTestsContactListManager *self) { switch (status) { @@ -422,9 +538,9 @@ status_changed_cb (TpBaseConnection *conn, static void constructed (GObject *object) { - TestContactListManager *self = TEST_CONTACT_LIST_MANAGER (object); + TpTestsContactListManager *self = TP_TESTS_CONTACT_LIST_MANAGER (object); void (*chain_up) (GObject *) = - ((GObjectClass *) test_contact_list_manager_parent_class)->constructed; + ((GObjectClass *) tp_tests_contact_list_manager_parent_class)->constructed; if (chain_up != NULL) { @@ -462,12 +578,22 @@ mutable_contact_groups_iface_init ( } static void -test_contact_list_manager_class_init (TestContactListManagerClass *klass) +mutable_iface_init (TpMutableContactListInterface *iface) +{ + iface->request_subscription_async = contact_list_request_subscription_async; + iface->authorize_publication_async = contact_list_authorize_publication_async; + iface->remove_contacts_async = contact_list_remove_contacts_async; + iface->unsubscribe_async = contact_list_unsubscribe_async; + iface->unpublish_async = contact_list_unpublish_async; +} + +static void +tp_tests_contact_list_manager_class_init (TpTestsContactListManagerClass *klass) { GObjectClass *object_class = (GObjectClass *) klass; TpBaseContactListClass *base_class =(TpBaseContactListClass *) klass; - g_type_class_add_private (klass, sizeof (TestContactListManagerPrivate)); + g_type_class_add_private (klass, sizeof (TpTestsContactListManagerPrivate)); object_class->constructed = constructed; object_class->dispose = dispose; @@ -477,7 +603,7 @@ test_contact_list_manager_class_init (TestContactListManagerClass *klass) } void -test_contact_list_manager_add_to_group (TestContactListManager *self, +tp_tests_contact_list_manager_add_to_group (TpTestsContactListManager *self, const gchar *group_name, TpHandle member) { TpBaseContactList *base = TP_BASE_CONTACT_LIST (self); @@ -485,12 +611,20 @@ test_contact_list_manager_add_to_group (TestContactListManager *self, g_hash_table_insert (d->groups, g_strdup (group_name), GUINT_TO_POINTER (1)); + if (g_hash_table_lookup (self->priv->groups, group_name) == NULL) + { + g_hash_table_insert (self->priv->groups, g_strdup (group_name), + GUINT_TO_POINTER (1)); + tp_base_contact_list_groups_created ((TpBaseContactList *) self, + &group_name, 1); + } + tp_base_contact_list_one_contact_groups_changed (base, member, &group_name, 1, NULL, 0); } void -test_contact_list_manager_remove_from_group (TestContactListManager *self, +tp_tests_contact_list_manager_remove_from_group (TpTestsContactListManager *self, const gchar *group_name, TpHandle member) { TpBaseContactList *base = TP_BASE_CONTACT_LIST (self); @@ -506,12 +640,12 @@ test_contact_list_manager_remove_from_group (TestContactListManager *self, } typedef struct { - TestContactListManager *self; + TpTestsContactListManager *self; TpHandleSet *handles; } SelfAndContact; static SelfAndContact * -self_and_contact_new (TestContactListManager *self, +self_and_contact_new (TpTestsContactListManager *self, TpHandleSet *handles) { SelfAndContact *ret = g_slice_new0 (SelfAndContact); @@ -593,7 +727,7 @@ receive_unauthorized (gpointer p) } void -test_contact_list_manager_request_subscription (TestContactListManager *self, +tp_tests_contact_list_manager_request_subscription (TpTestsContactListManager *self, guint n_members, TpHandle *members, const gchar *message) { TpHandleSet *handles; @@ -636,7 +770,7 @@ test_contact_list_manager_request_subscription (TestContactListManager *self, } void -test_contact_list_manager_unsubscribe (TestContactListManager *self, +tp_tests_contact_list_manager_unsubscribe (TpTestsContactListManager *self, guint n_members, TpHandle *members) { TpHandleSet *handles; @@ -661,7 +795,7 @@ test_contact_list_manager_unsubscribe (TestContactListManager *self, } void -test_contact_list_manager_authorize_publication (TestContactListManager *self, +tp_tests_contact_list_manager_authorize_publication (TpTestsContactListManager *self, guint n_members, TpHandle *members) { TpHandleSet *handles; @@ -687,7 +821,7 @@ test_contact_list_manager_authorize_publication (TestContactListManager *self, } void -test_contact_list_manager_unpublish (TestContactListManager *self, +tp_tests_contact_list_manager_unpublish (TpTestsContactListManager *self, guint n_members, TpHandle *members) { TpHandleSet *handles; @@ -713,7 +847,7 @@ test_contact_list_manager_unpublish (TestContactListManager *self, } void -test_contact_list_manager_remove (TestContactListManager *self, +tp_tests_contact_list_manager_remove (TpTestsContactListManager *self, guint n_members, TpHandle *members) { TpHandleSet *handles; @@ -739,7 +873,7 @@ test_contact_list_manager_remove (TestContactListManager *self, } void -test_contact_list_manager_add_initial_contacts (TestContactListManager *self, +tp_tests_contact_list_manager_add_initial_contacts (TpTestsContactListManager *self, guint n_members, TpHandle *members) { TpHandleSet *handles; diff --git a/tests/lib/contact-list-manager.h b/tests/lib/contact-list-manager.h index d54bb9938..8be787c34 100644 --- a/tests/lib/contact-list-manager.h +++ b/tests/lib/contact-list-manager.h @@ -9,61 +9,61 @@ * notice and this notice are preserved. */ -#ifndef __TEST_CONTACT_LIST_MANAGER_H__ -#define __TEST_CONTACT_LIST_MANAGER_H__ +#ifndef __TP_TESTS_CONTACT_LIST_MANAGER_H__ +#define __TP_TESTS_CONTACT_LIST_MANAGER_H__ #include <telepathy-glib/base-contact-list.h> G_BEGIN_DECLS -#define TEST_TYPE_CONTACT_LIST_MANAGER \ - (test_contact_list_manager_get_type ()) -#define TEST_CONTACT_LIST_MANAGER(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), TEST_TYPE_CONTACT_LIST_MANAGER, \ - TestContactListManager)) -#define TEST_CONTACT_LIST_MANAGER_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass), TEST_TYPE_CONTACT_LIST_MANAGER, \ - TestContactListManagerClass)) -#define TEST_IS_CONTACT_LIST_MANAGER(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj), TEST_TYPE_CONTACT_LIST_MANAGER)) -#define TEST_IS_CONTACT_LIST_MANAGER_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE((klass), TEST_TYPE_CONTACT_LIST_MANAGER)) -#define TEST_CONTACT_LIST_MANAGER_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS ((obj), TEST_TYPE_CONTACT_LIST_MANAGER, \ - TestContactListManagerClass)) +#define TP_TESTS_TYPE_CONTACT_LIST_MANAGER \ + (tp_tests_contact_list_manager_get_type ()) +#define TP_TESTS_CONTACT_LIST_MANAGER(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), TP_TESTS_TYPE_CONTACT_LIST_MANAGER, \ + TpTestsContactListManager)) +#define TP_TESTS_CONTACT_LIST_MANAGER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), TP_TESTS_TYPE_CONTACT_LIST_MANAGER, \ + TpTestsContactListManagerClass)) +#define TP_TESTS_IS_CONTACT_LIST_MANAGER(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), TP_TESTS_TYPE_CONTACT_LIST_MANAGER)) +#define TP_TESTS_IS_CONTACT_LIST_MANAGER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), TP_TESTS_TYPE_CONTACT_LIST_MANAGER)) +#define TP_TESTS_CONTACT_LIST_MANAGER_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), TP_TESTS_TYPE_CONTACT_LIST_MANAGER, \ + TpTestsContactListManagerClass)) -typedef struct _TestContactListManager TestContactListManager; -typedef struct _TestContactListManagerClass TestContactListManagerClass; -typedef struct _TestContactListManagerPrivate TestContactListManagerPrivate; +typedef struct _TpTestsContactListManager TpTestsContactListManager; +typedef struct _TpTestsContactListManagerClass TpTestsContactListManagerClass; +typedef struct _TpTestsContactListManagerPrivate TpTestsContactListManagerPrivate; -struct _TestContactListManagerClass { +struct _TpTestsContactListManagerClass { TpBaseContactListClass parent_class; }; -struct _TestContactListManager { +struct _TpTestsContactListManager { TpBaseContactList parent; - TestContactListManagerPrivate *priv; + TpTestsContactListManagerPrivate *priv; }; -GType test_contact_list_manager_get_type (void); +GType tp_tests_contact_list_manager_get_type (void); -void test_contact_list_manager_add_to_group (TestContactListManager *self, +void tp_tests_contact_list_manager_add_to_group (TpTestsContactListManager *self, const gchar *group_name, TpHandle member); -void test_contact_list_manager_remove_from_group (TestContactListManager *self, +void tp_tests_contact_list_manager_remove_from_group (TpTestsContactListManager *self, const gchar *group_name, TpHandle member); -void test_contact_list_manager_request_subscription (TestContactListManager *self, +void tp_tests_contact_list_manager_request_subscription (TpTestsContactListManager *self, guint n_members, TpHandle *members, const gchar *message); -void test_contact_list_manager_unsubscribe (TestContactListManager *self, +void tp_tests_contact_list_manager_unsubscribe (TpTestsContactListManager *self, guint n_members, TpHandle *members); -void test_contact_list_manager_authorize_publication (TestContactListManager *self, +void tp_tests_contact_list_manager_authorize_publication (TpTestsContactListManager *self, guint n_members, TpHandle *members); -void test_contact_list_manager_unpublish (TestContactListManager *self, +void tp_tests_contact_list_manager_unpublish (TpTestsContactListManager *self, guint n_members, TpHandle *members); -void test_contact_list_manager_remove (TestContactListManager *self, +void tp_tests_contact_list_manager_remove (TpTestsContactListManager *self, guint n_members, TpHandle *members); -void test_contact_list_manager_add_initial_contacts (TestContactListManager *self, +void tp_tests_contact_list_manager_add_initial_contacts (TpTestsContactListManager *self, guint n_members, TpHandle *members); G_END_DECLS diff --git a/tests/lib/contacts-conn.c b/tests/lib/contacts-conn.c index 6730bfa6a..4bfcafd1c 100644 --- a/tests/lib/contacts-conn.c +++ b/tests/lib/contacts-conn.c @@ -93,7 +93,7 @@ struct _TpTestsContactsConnectionPrivate GHashTable *contact_info; GPtrArray *default_contact_info; - TestContactListManager *list_manager; + TpTestsContactListManager *list_manager; }; typedef struct @@ -343,11 +343,40 @@ conn_contact_info_properties_getter (GObject *object, if (supported_fields == NULL) { supported_fields = g_ptr_array_new (); + + g_ptr_array_add (supported_fields, tp_value_array_build (4, + G_TYPE_STRING, "bday", + G_TYPE_STRV, NULL, + G_TYPE_UINT, 0, + G_TYPE_UINT, 1, + G_TYPE_INVALID)); + + g_ptr_array_add (supported_fields, tp_value_array_build (4, + G_TYPE_STRING, "email", + G_TYPE_STRV, NULL, + G_TYPE_UINT, 0, + G_TYPE_UINT, G_MAXUINT32, + G_TYPE_INVALID)); + + g_ptr_array_add (supported_fields, tp_value_array_build (4, + G_TYPE_STRING, "fn", + G_TYPE_STRV, NULL, + G_TYPE_UINT, 0, + G_TYPE_UINT, 1, + G_TYPE_INVALID)); + g_ptr_array_add (supported_fields, tp_value_array_build (4, - G_TYPE_STRING, "n", + G_TYPE_STRING, "tel", G_TYPE_STRV, NULL, G_TYPE_UINT, 0, + G_TYPE_UINT, G_MAXUINT32, + G_TYPE_INVALID)); + + g_ptr_array_add (supported_fields, tp_value_array_build (4, + G_TYPE_STRING, "url", + G_TYPE_STRV, NULL, G_TYPE_UINT, 0, + G_TYPE_UINT, G_MAXUINT32, G_TYPE_INVALID)); } g_value_set_boxed (value, supported_fields); @@ -386,7 +415,7 @@ constructed (GObject *object) if (parent_impl != NULL) parent_impl (object); - self->priv->list_manager = g_object_new (TEST_TYPE_CONTACT_LIST_MANAGER, + self->priv->list_manager = g_object_new (TP_TESTS_TYPE_CONTACT_LIST_MANAGER, "connection", self, NULL); tp_contacts_mixin_init (object, @@ -570,7 +599,7 @@ tp_tests_contacts_connection_class_init (TpTestsContactsConnectionClass *klass) tp_base_contact_list_mixin_class_init (base_class); } -TestContactListManager * +TpTestsContactListManager * tp_tests_contacts_connection_get_contact_list_manager ( TpTestsContactsConnection *self) { @@ -996,8 +1025,8 @@ my_set_contact_info (TpSvcConnectionInterfaceContactInfo *obj, g_ptr_array_add (copy, g_value_array_copy (g_ptr_array_index (info, i))); self_handle = tp_base_connection_get_self_handle (base); - g_hash_table_insert (self->priv->contact_info, GUINT_TO_POINTER (self_handle), - copy); + tp_tests_contacts_connection_change_contact_info (self, self_handle, copy); + g_ptr_array_unref (copy); tp_svc_connection_interface_contact_info_return_from_set_contact_info ( context); diff --git a/tests/lib/contacts-conn.h b/tests/lib/contacts-conn.h index b3f22fffb..71e17e12f 100644 --- a/tests/lib/contacts-conn.h +++ b/tests/lib/contacts-conn.h @@ -74,7 +74,7 @@ typedef enum { (G_TYPE_INSTANCE_GET_CLASS ((obj), TP_TESTS_TYPE_CONTACTS_CONNECTION, \ TpTestsContactsConnectionClass)) -TestContactListManager *tp_tests_contacts_connection_get_contact_list_manager ( +TpTestsContactListManager *tp_tests_contacts_connection_get_contact_list_manager ( TpTestsContactsConnection *self); void tp_tests_contacts_connection_change_aliases ( diff --git a/tests/lib/dbus-tube-chan.c b/tests/lib/dbus-tube-chan.c index 12d3c81dd..5d848f5dc 100644 --- a/tests/lib/dbus-tube-chan.c +++ b/tests/lib/dbus-tube-chan.c @@ -27,11 +27,25 @@ enum PROP_STATE, }; +enum +{ + SIG_NEW_CONNECTION, + LAST_SIGNAL +}; + +static guint _signals[LAST_SIGNAL] = { 0, }; + struct _TpTestsDBusTubeChannelPrivate { + /* Controls whether the channel should become open before returning from + * Open/Accept, after returning, or never. + */ + TpTestsDBusTubeChannelOpenMode open_mode; TpTubeChannelState state; /* TpHandle -> gchar * */ GHashTable *dbus_names; + + GDBusServer *dbus_server; }; static void @@ -108,6 +122,7 @@ tp_tests_dbus_tube_channel_init (TpTestsDBusTubeChannel *self) self->priv = G_TYPE_INSTANCE_GET_PRIVATE ((self), TP_TESTS_TYPE_DBUS_TUBE_CHANNEL, TpTestsDBusTubeChannelPrivate); + self->priv->open_mode = TP_TESTS_DBUS_TUBE_CHANNEL_OPEN_FIRST; self->priv->dbus_names = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free); } @@ -139,6 +154,20 @@ dispose (GObject *object) tp_clear_pointer (&self->priv->dbus_names, g_hash_table_unref); + if (self->priv->dbus_server != NULL) + { + /* FIXME: this is pretty stupid but apparently unless you start and then + * stop the server before freeing it, it doesn't stop listening. Calling + * _start() twice is a no-op. + * + * https://bugzilla.gnome.org/show_bug.cgi?id=673372 + */ + g_dbus_server_start (self->priv->dbus_server); + + g_dbus_server_stop (self->priv->dbus_server); + g_clear_object (&self->priv->dbus_server); + } + ((GObjectClass *) tp_tests_dbus_tube_channel_parent_class)->dispose ( object); } @@ -239,6 +268,15 @@ tp_tests_dbus_tube_channel_class_init (TpTestsDBusTubeChannelClass *klass) g_object_class_install_property (object_class, PROP_STATE, param_spec); + _signals[SIG_NEW_CONNECTION] = g_signal_new ("new-connection", + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_LAST, + 0, + g_signal_accumulator_true_handled, NULL, + NULL, + G_TYPE_BOOLEAN, + 1, G_TYPE_DBUS_CONNECTION); + tp_dbus_properties_mixin_implement_interface (object_class, TP_IFACE_QUARK_CHANNEL_TYPE_DBUS_TUBE, tp_dbus_properties_mixin_getter_gobject_properties, NULL, @@ -253,7 +291,6 @@ tp_tests_dbus_tube_channel_class_init (TpTestsDBusTubeChannelClass *klass) sizeof (TpTestsDBusTubeChannelPrivate)); } -#if 0 static void change_state (TpTestsDBusTubeChannel *self, TpTubeChannelState state) @@ -262,19 +299,107 @@ change_state (TpTestsDBusTubeChannel *self, tp_svc_channel_interface_tube_emit_tube_channel_state_changed (self, state); } -#endif + +static gboolean +dbus_new_connection_cb (GDBusServer *server, + GDBusConnection *connection, + gpointer user_data) +{ + TpTestsDBusTubeChannel *self = user_data; + gboolean ret = FALSE; + + g_signal_emit (self, _signals[SIG_NEW_CONNECTION], 0, connection, &ret); + return ret; +} + +static void +open_tube (TpTestsDBusTubeChannel *self) +{ + GError *error = NULL; + gchar *guid; + + guid = g_dbus_generate_guid (); + + self->priv->dbus_server = g_dbus_server_new_sync ( + "unix:abstract=dbus-tube-test", + G_DBUS_SERVER_FLAGS_NONE, guid, NULL, NULL, &error); + g_assert_no_error (error); + + g_free (guid); + + g_signal_connect (self->priv->dbus_server, "new-connection", + G_CALLBACK (dbus_new_connection_cb), self); +} + +static void +really_open_tube (TpTestsDBusTubeChannel *self) +{ + g_dbus_server_start (self->priv->dbus_server); + + change_state (self, TP_TUBE_CHANNEL_STATE_OPEN); +} + +static void +dbus_tube_offer (TpSvcChannelTypeDBusTube *chan, + GHashTable *parameters, + guint access_control, + DBusGMethodInvocation *context) +{ + TpTestsDBusTubeChannel *self = (TpTestsDBusTubeChannel *) chan; + + open_tube (self); + + if (self->priv->open_mode == TP_TESTS_DBUS_TUBE_CHANNEL_OPEN_FIRST) + really_open_tube (self); + + tp_svc_channel_type_dbus_tube_return_from_offer (context, + g_dbus_server_get_client_address (self->priv->dbus_server)); + + if (self->priv->open_mode == TP_TESTS_DBUS_TUBE_CHANNEL_OPEN_SECOND) + really_open_tube (self); + else if (self->priv->open_mode == TP_TESTS_DBUS_TUBE_CHANNEL_NEVER_OPEN) + tp_base_channel_close (TP_BASE_CHANNEL (self)); +} + +static void +dbus_tube_accept (TpSvcChannelTypeDBusTube *chan, + guint access_control, + DBusGMethodInvocation *context) +{ + TpTestsDBusTubeChannel *self = (TpTestsDBusTubeChannel *) chan; + + open_tube (self); + + if (self->priv->open_mode == TP_TESTS_DBUS_TUBE_CHANNEL_OPEN_FIRST) + really_open_tube (self); + + tp_svc_channel_type_dbus_tube_return_from_accept (context, + g_dbus_server_get_client_address (self->priv->dbus_server)); + + if (self->priv->open_mode == TP_TESTS_DBUS_TUBE_CHANNEL_OPEN_SECOND) + really_open_tube (self); + else if (self->priv->open_mode == TP_TESTS_DBUS_TUBE_CHANNEL_NEVER_OPEN) + tp_base_channel_close (TP_BASE_CHANNEL (self)); +} + +void +tp_tests_dbus_tube_channel_set_open_mode ( + TpTestsDBusTubeChannel *self, + TpTestsDBusTubeChannelOpenMode open_mode) +{ + self->priv->open_mode = open_mode; +} static void dbus_tube_iface_init (gpointer iface, gpointer data) { -#if 0 - /* TODO: implement methods */ TpSvcChannelTypeDBusTubeClass *klass = iface; #define IMPLEMENT(x) tp_svc_channel_type_dbus_tube_implement_##x (klass, dbus_tube_##x) + IMPLEMENT (offer); + IMPLEMENT (accept); #undef IMPLEMENT -#endif } /* Contact DBus Tube */ diff --git a/tests/lib/dbus-tube-chan.h b/tests/lib/dbus-tube-chan.h index 6e24ea8d3..0a6b1a794 100644 --- a/tests/lib/dbus-tube-chan.h +++ b/tests/lib/dbus-tube-chan.h @@ -51,6 +51,16 @@ struct _TpTestsDBusTubeChannel { TpTestsDBusTubeChannelPrivate *priv; }; +typedef enum { + TP_TESTS_DBUS_TUBE_CHANNEL_OPEN_FIRST, + TP_TESTS_DBUS_TUBE_CHANNEL_OPEN_SECOND, + TP_TESTS_DBUS_TUBE_CHANNEL_NEVER_OPEN +} TpTestsDBusTubeChannelOpenMode; + +void tp_tests_dbus_tube_channel_set_open_mode ( + TpTestsDBusTubeChannel *self, + TpTestsDBusTubeChannelOpenMode open_mode); + /* Contact DBus Tube */ typedef struct _TpTestsContactDBusTubeChannel TpTestsContactDBusTubeChannel; diff --git a/tests/lib/simple-account-manager.c b/tests/lib/simple-account-manager.c index ca97f1556..ddf744c78 100644 --- a/tests/lib/simple-account-manager.c +++ b/tests/lib/simple-account-manager.c @@ -1,7 +1,7 @@ /* * simple-account-manager.c - a simple account manager service. * - * Copyright (C) 2007-2009 Collabora Ltd. <http://www.collabora.co.uk/> + * Copyright (C) 2007-2012 Collabora Ltd. <http://www.collabora.co.uk/> * Copyright (C) 2007-2008 Nokia Corporation * * Copying and distribution of this file, with or without modification, @@ -34,14 +34,6 @@ G_DEFINE_TYPE_WITH_CODE (TpTestsSimpleAccountManager, /* TP_IFACE_ACCOUNT_MANAGER is implied */ static const char *ACCOUNT_MANAGER_INTERFACES[] = { NULL }; -static gchar *USABLE_ACCOUNTS[] = { - "/im/telepathy1/Account/fakecm/fakeproto/usableaccount", - NULL }; - -static gchar *UNUSABLE_ACCOUNTS[] = { - "/im/telepathy1/Account/fakecm/fakeproto/unusableaccount", - NULL }; - enum { PROP_0, @@ -53,6 +45,7 @@ enum struct _TpTestsSimpleAccountManagerPrivate { GPtrArray *usable_accounts; + GPtrArray *unusable_accounts; }; static void @@ -83,15 +76,11 @@ account_manager_iface_init (gpointer klass, static void tp_tests_simple_account_manager_init (TpTestsSimpleAccountManager *self) { - guint i; - self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, TP_TESTS_TYPE_SIMPLE_ACCOUNT_MANAGER, TpTestsSimpleAccountManagerPrivate); self->priv->usable_accounts = g_ptr_array_new_with_free_func (g_free); - - for (i = 0; USABLE_ACCOUNTS[i] != NULL; i++) - g_ptr_array_add (self->priv->usable_accounts, g_strdup (USABLE_ACCOUNTS[i])); + self->priv->unusable_accounts = g_ptr_array_new_with_free_func (g_free); } static void @@ -101,8 +90,6 @@ tp_tests_simple_account_manager_get_property (GObject *object, GParamSpec *spec) { TpTestsSimpleAccountManager *self = SIMPLE_ACCOUNT_MANAGER (object); - GPtrArray *accounts; - guint i = 0; switch (property_id) { case PROP_INTERFACES: @@ -114,12 +101,7 @@ tp_tests_simple_account_manager_get_property (GObject *object, break; case PROP_UNUSABLE_ACCOUNTS: - accounts = g_ptr_array_new (); - - for (i=0; UNUSABLE_ACCOUNTS[i] != NULL; i++) - g_ptr_array_add (accounts, g_strdup (UNUSABLE_ACCOUNTS[i])); - - g_value_take_boxed (value, accounts); + g_value_set_boxed (value, self->priv->unusable_accounts); break; default: @@ -129,13 +111,14 @@ tp_tests_simple_account_manager_get_property (GObject *object, } static void -tp_tests_simple_account_manager_dispose (GObject *object) +tp_tests_simple_account_manager_finalize (GObject *object) { TpTestsSimpleAccountManager *self = SIMPLE_ACCOUNT_MANAGER (object); - tp_clear_pointer (&self->priv->usable_accounts, g_ptr_array_unref); + g_ptr_array_unref (self->priv->usable_accounts); + g_ptr_array_unref (self->priv->unusable_accounts); - G_OBJECT_CLASS (tp_tests_simple_account_manager_parent_class)->dispose ( + G_OBJECT_CLASS (tp_tests_simple_account_manager_parent_class)->finalize ( object); } @@ -173,8 +156,8 @@ tp_tests_simple_account_manager_class_init ( }; g_type_class_add_private (klass, sizeof (TpTestsSimpleAccountManagerPrivate)); + object_class->finalize = tp_tests_simple_account_manager_finalize; object_class->get_property = tp_tests_simple_account_manager_get_property; - object_class->dispose = tp_tests_simple_account_manager_dispose; param_spec = g_param_spec_boxed ("interfaces", "Extra D-Bus interfaces", "In this case we only implement AccountManager, so none.", @@ -197,12 +180,44 @@ tp_tests_simple_account_manager_class_init ( G_STRUCT_OFFSET (TpTestsSimpleAccountManagerClass, dbus_props_class)); } +static void +remove_from_array (GPtrArray *array, const gchar *str) +{ + guint i; + + for (i = 0; i < array->len; i++) + if (!tp_strdiff (str, g_ptr_array_index (array, i))) + { + g_ptr_array_remove_index_fast (array, i); + return; + } +} + +void +tp_tests_simple_account_manager_add_account ( + TpTestsSimpleAccountManager *self, + const gchar *object_path, + gboolean usable) +{ + remove_from_array (self->priv->usable_accounts, object_path); + remove_from_array (self->priv->unusable_accounts, object_path); + + if (usable) + g_ptr_array_add (self->priv->usable_accounts, g_strdup (object_path)); + else + g_ptr_array_add (self->priv->unusable_accounts, g_strdup (object_path)); + + tp_svc_account_manager_emit_account_usability_changed (self, object_path, + usable); +} + void -tp_tests_simple_account_manager_set_usable_accounts ( +tp_tests_simple_account_manager_remove_account ( TpTestsSimpleAccountManager *self, - GPtrArray *accounts) + const gchar *object_path) { - tp_clear_pointer (&self->priv->usable_accounts, g_ptr_array_unref); + remove_from_array (self->priv->usable_accounts, object_path); + remove_from_array (self->priv->unusable_accounts, object_path); - self->priv->usable_accounts = g_ptr_array_ref (accounts); + tp_svc_account_manager_emit_account_removed (self, object_path); } diff --git a/tests/lib/simple-account-manager.h b/tests/lib/simple-account-manager.h index 32f9aeda9..eb3beee53 100644 --- a/tests/lib/simple-account-manager.h +++ b/tests/lib/simple-account-manager.h @@ -1,7 +1,7 @@ /* * simple-account-manager.h - header for a simple account manager service. * - * Copyright (C) 2007-2009 Collabora Ltd. <http://www.collabora.co.uk/> + * Copyright (C) 2007-2012 Collabora Ltd. <http://www.collabora.co.uk/> * Copyright (C) 2007-2008 Nokia Corporation * * Copying and distribution of this file, with or without modification, @@ -52,9 +52,14 @@ GType tp_tests_simple_account_manager_get_type (void); (G_TYPE_INSTANCE_GET_CLASS ((obj), TP_TESTS_TYPE_SIMPLE_ACCOUNT_MANAGER, \ TpTestsSimpleAccountManagerClass)) -void tp_tests_simple_account_manager_set_usable_accounts ( +void tp_tests_simple_account_manager_add_account ( TpTestsSimpleAccountManager *self, - GPtrArray *accounts); + const gchar *object_path, + gboolean usable); + +void tp_tests_simple_account_manager_remove_account ( + TpTestsSimpleAccountManager *self, + const gchar *object_path); G_END_DECLS diff --git a/tests/lib/simple-account.c b/tests/lib/simple-account.c index d746079b9..106627c22 100644 --- a/tests/lib/simple-account.c +++ b/tests/lib/simple-account.c @@ -78,6 +78,8 @@ struct _TpTestsSimpleAccountPrivate TpConnectionPresenceType presence; gchar *presence_status; gchar *presence_msg; + gchar *connection_path; + gboolean enabled; }; static void @@ -130,6 +132,8 @@ tp_tests_simple_account_init (TpTestsSimpleAccount *self) self->priv->presence = TP_CONNECTION_PRESENCE_TYPE_AWAY; self->priv->presence_status = g_strdup ("currently-away"); self->priv->presence_msg = g_strdup ("this is my CurrentPresence"); + self->priv->connection_path = g_strdup ("/"); + self->priv->enabled = TRUE; } /* you may have noticed this is not entirely realistic */ @@ -161,7 +165,7 @@ tp_tests_simple_account_get_property (GObject *object, g_value_set_boolean (value, TRUE); break; case PROP_ENABLED: - g_value_set_boolean (value, TRUE); + g_value_set_boolean (value, self->priv->enabled); break; case PROP_NICKNAME: g_value_set_string (value, "badger"); @@ -180,7 +184,7 @@ tp_tests_simple_account_get_property (GObject *object, g_value_set_boolean (value, FALSE); break; case PROP_CONNECTION: - g_value_set_boxed (value, "/"); + g_value_set_boxed (value, self->priv->connection_path); break; case PROP_CONNECTION_STATUS: g_value_set_uint (value, TP_CONNECTION_STATUS_CONNECTED); @@ -535,3 +539,41 @@ tp_tests_simple_account_set_presence (TpTestsSimpleAccount *self, g_boxed_free (TP_STRUCT_TYPE_PRESENCE, v); } + +void +tp_tests_simple_account_set_connection (TpTestsSimpleAccount *self, + const gchar *object_path) +{ + GHashTable *change; + + if (object_path == NULL) + object_path = "/"; + + g_free (self->priv->connection_path); + self->priv->connection_path = g_strdup (object_path); + + change = tp_asv_new (NULL, NULL); + tp_asv_set_string (change, "Connection", object_path); + tp_svc_account_emit_account_property_changed (self, change); + g_hash_table_unref (change); +} + +void +tp_tests_simple_account_removed (TpTestsSimpleAccount *self) +{ + tp_svc_account_emit_removed (self); +} + +void +tp_tests_simple_account_set_enabled (TpTestsSimpleAccount *self, + gboolean enabled) +{ + GHashTable *change; + + self->priv->enabled = enabled; + + change = tp_asv_new (NULL, NULL); + tp_asv_set_boolean (change, "Enabled", enabled); + tp_svc_account_emit_account_property_changed (self, change); + g_hash_table_unref (change); +} diff --git a/tests/lib/simple-account.h b/tests/lib/simple-account.h index 40f708c9c..2ce3efd12 100644 --- a/tests/lib/simple-account.h +++ b/tests/lib/simple-account.h @@ -1,7 +1,7 @@ /* * simple-account.h - header for a simple account service. * - * Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/> + * Copyright (C) 2010-2012 Collabora Ltd. <http://www.collabora.co.uk/> * * Copying and distribution of this file, with or without modification, * are permitted in any medium without royalty provided the copyright @@ -57,6 +57,13 @@ void tp_tests_simple_account_set_presence (TpTestsSimpleAccount *self, const gchar *status, const gchar *message); +void tp_tests_simple_account_set_connection (TpTestsSimpleAccount *self, + const gchar *object_path); + +void tp_tests_simple_account_removed (TpTestsSimpleAccount *self); +void tp_tests_simple_account_set_enabled (TpTestsSimpleAccount *self, + gboolean enabled); + G_END_DECLS #endif /* #ifndef __TP_TESTS_SIMPLE_ACCOUNT_H__ */ |