summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon McVittie <simon.mcvittie@collabora.co.uk>2012-04-10 13:51:53 +0100
committerSimon McVittie <simon.mcvittie@collabora.co.uk>2012-04-10 13:51:53 +0100
commit7238bf9925a4bf62bec867af49f5f82e4f6bf153 (patch)
treef763c11515d06f6d02ff843d43582a65aff696f3
parentd55fb31b091a5d2bc0d1664378e8a5a0230c8c83 (diff)
parentb0919cddbde87259613b0f9c56fb9f25b6065b12 (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
-rw-r--r--.gitignore2
-rw-r--r--Android.mk4
-rw-r--r--Makefile.am2
-rw-r--r--NEWS70
-rw-r--r--configure.ac1
-rw-r--r--docs/reference/telepathy-glib-sections.txt23
-rw-r--r--examples/client/Makefile.am2
-rw-r--r--examples/client/approver.c23
-rw-r--r--examples/client/dbus-tubes/Makefile.am25
-rw-r--r--examples/client/dbus-tubes/accepter.c220
-rw-r--r--examples/client/dbus-tubes/constants.h9
-rw-r--r--examples/client/dbus-tubes/offerer.c238
-rw-r--r--examples/client/media-observer.c12
-rwxr-xr-xexamples/client/python/file-transfer.py8
-rw-r--r--examples/client/stream-tubes/Makefile.am4
-rw-r--r--telepathy-glib/Makefile.am3
-rw-r--r--telepathy-glib/account.c10
-rw-r--r--telepathy-glib/automatic-client-factory.c144
-rw-r--r--telepathy-glib/base-call-internal.h4
-rw-r--r--telepathy-glib/base-call-stream.c57
-rw-r--r--telepathy-glib/base-contact-list-internal.h31
-rw-r--r--telepathy-glib/base-contact-list.c179
-rw-r--r--telepathy-glib/base-contact-list.h28
-rw-r--r--telepathy-glib/base-media-call-channel.c79
-rw-r--r--telepathy-glib/base-media-call-channel.h3
-rw-r--r--telepathy-glib/base-media-call-content.c3
-rw-r--r--telepathy-glib/base-media-call-stream.c104
-rw-r--r--telepathy-glib/base-media-call-stream.h2
-rw-r--r--telepathy-glib/call-channel.c257
-rw-r--r--telepathy-glib/call-channel.h16
-rw-r--r--telepathy-glib/call-content-media-description.c612
-rw-r--r--telepathy-glib/call-content-media-description.h43
-rw-r--r--telepathy-glib/call-content.c28
-rw-r--r--telepathy-glib/call-stream.c29
-rw-r--r--telepathy-glib/capabilities.c6
-rw-r--r--telepathy-glib/channel.c24
-rw-r--r--telepathy-glib/connection-manager.c12
-rw-r--r--telepathy-glib/contact.c10
-rw-r--r--telepathy-glib/dbus-tube-channel.c480
-rw-r--r--telepathy-glib/dbus-tube-channel.h25
-rw-r--r--telepathy-glib/introspection.am8
-rw-r--r--telepathy-glib/protocol.c6
-rw-r--r--telepathy-glib/proxy.c10
-rw-r--r--telepathy-glib/stream-tube-channel.c11
-rw-r--r--telepathy-glib/text-channel.c21
-rw-r--r--telepathy-glib/versions/0.17.6.abi32
-rw-r--r--telepathy-glib/versions/0.17.7.abi5
-rw-r--r--telepathy-glib/versions/0.18.0.abi12
-rw-r--r--tests/dbus/account-manager.c13
-rw-r--r--tests/dbus/contact-lists.c22
-rw-r--r--tests/dbus/contacts.c77
-rw-r--r--tests/dbus/dbus-tube.c268
-rw-r--r--tests/lib/contact-list-manager.c204
-rw-r--r--tests/lib/contact-list-manager.h64
-rw-r--r--tests/lib/contacts-conn.c41
-rw-r--r--tests/lib/contacts-conn.h2
-rw-r--r--tests/lib/dbus-tube-chan.c135
-rw-r--r--tests/lib/dbus-tube-chan.h10
-rw-r--r--tests/lib/simple-account-manager.c75
-rw-r--r--tests/lib/simple-account-manager.h11
-rw-r--r--tests/lib/simple-account.c46
-rw-r--r--tests/lib/simple-account.h9
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
diff --git a/NEWS b/NEWS
index cecd297fa..4018b5f7d 100644
--- a/NEWS
+++ b/NEWS
@@ -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 (&params, 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__ */