diff options
author | Xavier Claessens <xavier.claessens@collabora.co.uk> | 2012-05-09 20:45:36 +0200 |
---|---|---|
committer | Xavier Claessens <xavier.claessens@collabora.co.uk> | 2012-05-09 22:06:44 +0200 |
commit | ebd725a6d90cfdca41d2433e671af79baf68f4f4 (patch) | |
tree | 929d3dba110cafc4315cf7c7489918b93fb15e9e | |
parent | f4ad3d7c7bb697d0e4cd73fd88583c375dea643c (diff) | |
parent | 53cf9df185364f552d60415de885936ed6a51b81 (diff) |
Merge branch 'master' into next
Conflicts:
configure.ac
docs/reference/telepathy-glib-sections.txt
examples/client/extended-client.c
examples/client/inspect-contact.c
examples/cm/echo-message-parts/chan.c
spec/Protocol_Interface_Presence1.xml
spec/all.xml
telepathy-glib/Makefile.am
telepathy-glib/abi.am
telepathy-glib/base-connection.c
telepathy-glib/base-connection.h
telepathy-glib/connection-contact-list.c
telepathy-glib/contacts-mixin.c
telepathy-glib/intset.c
telepathy-glib/message-mixin.c
telepathy-glib/message-mixin.h
telepathy-glib/stream-tube-channel.c
telepathy-glib/text-channel.c
tests/lib/util.c
60 files changed, 3749 insertions, 281 deletions
diff --git a/.gitignore b/.gitignore index 546bff798..c8c5ed200 100644 --- a/.gitignore +++ b/.gitignore @@ -79,6 +79,7 @@ stamp-h1 tags /telepathy-glib-0.* /telepathy-glib/tmp-introspect*/ +telepathy-glib/version.h tests/dbus/dbus-installed/session.conf tests/dbus/dbus-uninstalled/session.conf tests/dbus/run-test.sh @@ -1,4 +1,4 @@ -telepathy-glib 0.19.0 (UNRELEASED) +telepathy-glib 0.19.0 (2012-05-09) ================================== Dependencies: @@ -9,23 +9,40 @@ Deprecations: • Deprecations are now versioned. telepathy-glib users can define TP_VERSION_MIN_REQUIRED and/or TP_VERSION_MAX_ALLOWED, which work like the - corresponding macros in GLib 2.32. + corresponding macros in GLib 2.32. (Simon) • All TpChannel APIs using contact TpHandle have been deprecated in favor of their TpContact variants. Note that replacement APIs are only guaranteed to work with Connection Managers implementing spec >= 0.23.4. Any CMs using telepathy-glib's TpGroupMixin for implementing the channel's group iface - are fine. + are fine. (Xavier) -• TpTextMixin is (officially) deprecated, use TpMessageMixin. +• TpTextMixin is (officially) deprecated, use TpMessageMixin. (Xavier) • TpIntsetIter is deprecated, use TpIntsetFastIter. The typedefs - TpIntSetIter and TpIntSetFastIter are also deprecated. + TpIntSetIter and TpIntSetFastIter are also deprecated. (Simon) • TP_ERRORS has officially been deprecated since 0.11; it now produces - deprecation warnings too. + deprecation warnings too. (Simon) -• Reimplementation of the RequestHandles method is deprecated. +• Reimplementation of the RequestHandles method is deprecated. (Simon) + +• tp_connection_get_contacts_by_id is deprecated and replaced by + tp_connection_dup_contact_by_id_async, for proper GAsyncResult API, and is + now for single identifier to simplify most common use case. + (fd.o #27687 and #30874, Xavier) + +• tp_connection_get_contacts_by_handle() is deprecated with no replacement. It + is deprecated to create a TpContact without knowing both its id and handle. + (fd.o #27687 and #30874, Xavier) + +• tp_connection_upgrade_contacts is deprecated and replaced by + tp_connection_upgrade_contacts_async, for proper GAsyncResult API. Note that + the connection must implement the Contacts interface to use this new API. + (fd.o #27687 and #30874, Xavier) + +• TP_CHANNEL_FEATURE_CHAT_STATES and its corresponding APIs are deprecated and + replaced by similar API on TpTextChannel. Enhancements: @@ -37,7 +54,7 @@ Enhancements: • A new meta-header, <telepathy-glib/telepathy-glib-dbus.h>, now includes all generated code. Please include it in any file that uses tp_svc_*, tp_cli_*, TP_IFACE_*, TP_HASH_TYPE_*, TP_STRUCT_TYPE_* or TP_ARRAY_TYPE_*. - In telepathy-glib 1.0, it will become a separate pkg-config module. + In telepathy-glib 1.0, it will become a separate pkg-config module. (Simon) • Replace --disable-coding-style-checks and --disable-doc-checks with --disable-fatal-warnings. In addition to what the removed options did, @@ -73,6 +90,11 @@ Enhancements: clean, but less thorough than distclean (in particular, it doesn't forget your ./configure options) (Simon) +• Add TpTLSCertificate, a TpProxy subclass representing a TLS + certificate (fdo #30460, Guillaume) + +• TpMessageMixin now has helpers to implement the ChatState interface. + Fixes: • Make it safe to hold refs to a remaining GAsyncResult after returning diff --git a/docs/reference/telepathy-glib-docs.sgml b/docs/reference/telepathy-glib-docs.sgml index b5e33e0fc..0ee30c5f0 100644 --- a/docs/reference/telepathy-glib-docs.sgml +++ b/docs/reference/telepathy-glib-docs.sgml @@ -61,6 +61,8 @@ <xi:include href="xml/debug-message.xml"/> <xi:include href="xml/room-list.xml"/> <xi:include href="xml/room-info.xml"/> + <xi:include href="xml/tls-certificate.xml"/> + <xi:include href="xml/tls-certificate-rejection.xml"/> </chapter> <chapter id="ch-service-base"> diff --git a/docs/reference/telepathy-glib-sections.txt b/docs/reference/telepathy-glib-sections.txt index 02a616751..9ab85be16 100644 --- a/docs/reference/telepathy-glib-sections.txt +++ b/docs/reference/telepathy-glib-sections.txt @@ -946,6 +946,9 @@ TpSvcConnectionInterfaceContactsClass tp_svc_connection_interface_contacts_get_contact_attributes_impl tp_svc_connection_interface_contacts_implement_get_contact_attributes tp_svc_connection_interface_contacts_return_from_get_contact_attributes +tp_svc_connection_interface_contacts_get_contact_by_id_impl +tp_svc_connection_interface_contacts_implement_get_contact_by_id +tp_svc_connection_interface_contacts_return_from_get_contact_by_id <SUBSECTION Standard> TP_IS_SVC_CONNECTION_INTERFACE_CONTACTS TP_SVC_CONNECTION_INTERFACE_CONTACTS @@ -2064,6 +2067,12 @@ tp_message_mixin_set_rescued tp_message_mixin_take_received tp_message_mixin_has_pending_messages tp_message_mixin_clear +<SUBSECTION> +TpMessageMixinSendChatStateImpl +tp_message_mixin_chat_state_iface_init +tp_message_mixin_change_chat_state +tp_message_mixin_implement_send_chat_state +tp_message_mixin_maybe_send_gone <SUBSECTION Private> TpMessageMixinPrivate </SECTION> @@ -3528,6 +3537,8 @@ tp_cli_connection_interface_cellular_signal_callback_imsi_changed <INCLUDE>telepathy-glib/telepathy-glib-dbus.h</INCLUDE> tp_cli_connection_interface_contacts_call_get_contact_attributes tp_cli_connection_interface_contacts_callback_for_get_contact_attributes +tp_cli_connection_interface_contacts_call_get_contact_by_id +tp_cli_connection_interface_contacts_callback_for_get_contact_by_id </SECTION> <SECTION> @@ -3821,6 +3832,12 @@ TP_CONTACT_FEATURE_SUBSCRIPTION_STATES tp_contact_get_subscribe_state tp_contact_get_publish_state tp_contact_get_publish_request +<SUBSECTION> +tp_connection_dup_contact_by_id_async +tp_connection_dup_contact_by_id_finish +tp_connection_upgrade_contacts_async +tp_connection_upgrade_contacts_finish +<SUBSECTION operations> tp_contact_request_subscription_async tp_contact_request_subscription_finish tp_contact_authorize_publication_async @@ -5343,11 +5360,15 @@ tp_text_channel_ack_all_pending_messages_finish tp_text_channel_set_chat_state_async tp_text_channel_set_chat_state_finish tp_text_channel_supports_message_type +<SUBSECTION> TP_TEXT_CHANNEL_FEATURE_SMS tp_text_channel_is_sms_channel tp_text_channel_get_sms_flash tp_text_channel_get_sms_length_async tp_text_channel_get_sms_length_finish +<SUBSECTION> +TP_TEXT_CHANNEL_FEATURE_CHAT_STATES +tp_text_channel_get_chat_state <SUBSECTION Standard> TP_IS_TEXT_CHANNEL TP_IS_TEXT_CHANNEL_CLASS @@ -5360,6 +5381,7 @@ TpTextChannelPrivate <SUBSECTION Private> tp_text_channel_get_feature_quark_incoming_messages tp_text_channel_get_feature_quark_sms +tp_text_channel_get_feature_quark_chat_states </SECTION> <SECTION> @@ -6111,3 +6133,68 @@ tp_room_info_get_type <SUBSECTION Private> TpRoomInfoPriv </SECTION> + +<SECTION> +<FILE>tls-certificate</FILE> +<INCLUDE>telepathy-glib/telepathy-glib.h</INCLUDE> +<TITLE>TpTLSCertificate</TITLE> +TpTLSCertificate +tp_tls_certificate_init_known_interfaces +tp_tls_certificate_new +TP_TLS_CERTIFICATE_FEATURE_CORE +tp_tls_certificate_get_rejection +tp_tls_certificate_get_nth_rejection +tp_tls_certificate_accept_async +tp_tls_certificate_accept_finish +tp_tls_certificate_add_rejection +tp_tls_certificate_reject_async +tp_tls_certificate_reject_finish +tp_tls_certificate_get_cert_type +tp_tls_certificate_get_cert_data +tp_tls_certificate_get_state +<SUBSECTION Private> +tp_cli_authentication_tls_certificate_call_accept +tp_cli_authentication_tls_certificate_call_reject +tp_cli_authentication_tls_certificate_callback_for_accept +tp_cli_authentication_tls_certificate_callback_for_reject +tp_cli_authentication_tls_certificate_connect_to_accepted +tp_cli_authentication_tls_certificate_connect_to_rejected +tp_cli_authentication_tls_certificate_signal_callback_accepted +tp_cli_authentication_tls_certificate_signal_callback_rejected +tp_cli_tls_cert_add_signals +<SUBSECTION Standard> +tp_tls_certificate_get_feature_quark_core +TP_IS_TLS_CERTIFICATE +TP_IS_TLS_CERTIFICATE_CLASS +TP_TLS_CERTIFICATE +TP_TLS_CERTIFICATE_CLASS +TP_TLS_CERTIFICATE_GET_CLASS +TP_TYPE_TLS_CERTIFICATE +TpTLSCertificateClass +TpTLSCertificateClassPrivate +TpTLSCertificatePrivate +tp_tls_certificate_get_type +</SECTION> + +<SECTION> +<FILE>tls-certificate-rejection</FILE> +<INCLUDE>telepathy-glib/telepathy-glib.h</INCLUDE> +<TITLE>TpTLSCertificateRejection</TITLE> +TpTLSCertificateRejection +tp_tls_certificate_rejection_get_dbus_error +tp_tls_certificate_rejection_get_details +tp_tls_certificate_rejection_get_error +tp_tls_certificate_rejection_get_reason +tp_tls_certificate_rejection_raise_error +<SUBSECTION Standard> +TP_IS_TLS_CERTIFICATE_REJECTION +TP_IS_TLS_CERTIFICATE_REJECTION_CLASS +TP_TLS_CERTIFICATE_REJECTION +TP_TLS_CERTIFICATE_REJECTION_CLASS +TP_TLS_CERTIFICATE_REJECTION_GET_CLASS +TP_TYPE_TLS_CERTIFICATE_REJECTION +TpTLSCertificateRejectionClass +tp_tls_certificate_rejection_get_type +<SUBSECTION Private> +TpTLSCertificateRejectionPriv +</SECTION> diff --git a/examples/client/extended-client.c b/examples/client/extended-client.c index 4750986dc..9caa0d567 100644 --- a/examples/client/extended-client.c +++ b/examples/client/extended-client.c @@ -120,36 +120,25 @@ contact_pair_free (gpointer p) } static void -contacts_ready_cb (TpConnection *conn, - guint n_contacts, - TpContact * const *contacts, - const gchar * const *requested_ids, - GHashTable *failed_id_errors, - const GError *general_error, - gpointer user_data, - GObject *weak_object) +contact_ready_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) { - GHashTableIter iter; - gpointer k, v; + TpConnection *conn = (TpConnection *) object; GHashTable *asv; ContactPair *pair; + GError *error = NULL; - /* This runs if tp_connection_get_contacts_by_id failed completely (e.g. - * the CM crashed) */ - if (die_if (general_error, "tp_connection_get_contacts_by_id()")) - return; - - /* If any making a TpContact for one of the requested IDs fails, they'll - * be present in this hash table with an error as value */ - g_hash_table_iter_init (&iter, failed_id_errors); + pair = g_slice_new0 (ContactPair); + pair->contacts[0] = tp_connection_dup_contact_by_id_finish (conn, + result, &error); + pair->contacts[1] = g_object_ref (tp_connection_get_self_contact (conn)); - while (g_hash_table_iter_next (&iter, &k, &v)) + if (die_if (error, "tp_connection_dup_contact_by_id_async()")) { - const gchar *failed_id = k; - const GError *contact_error = v; - - if (die_if (contact_error, failed_id)) - return; + g_clear_error (&error); + contact_pair_free (pair); + return; } asv = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, @@ -157,10 +146,6 @@ contacts_ready_cb (TpConnection *conn, g_hash_table_insert (asv, "previous-owner", tp_g_value_slice_new_static_string ("Shadowman")); - pair = g_slice_new0 (ContactPair); - pair->contacts[0] = g_object_ref (contacts[0]); - pair->contacts[1] = g_object_ref (contacts[1]); - example_cli_connection_interface_hats_call_set_hat (conn, -1, "red", EXAMPLE_HAT_STYLE_FEDORA, asv, set_hat_cb, pair, contact_pair_free, NULL); @@ -174,7 +159,6 @@ conn_ready (GObject *source, gpointer user_data) { GError *error = NULL; - static const gchar * const names[] = { "myself@server", "other@server" }; TpConnection *conn = TP_CONNECTION (source); if (!tp_proxy_prepare_finish (conn, result, &error)) @@ -193,9 +177,9 @@ conn_ready (GObject *source, return; } - /* Get contact objects for myself and someone else */ - tp_connection_get_contacts_by_id (conn, 2, names, NULL, - contacts_ready_cb, NULL, NULL, NULL); + /* Get contact object for someone else */ + tp_connection_dup_contact_by_id_async (conn, "other@server", NULL, + contact_ready_cb, NULL); } static void diff --git a/examples/client/inspect-contact.c b/examples/client/inspect-contact.c index 0d7464ba9..9fe9681ee 100644 --- a/examples/client/inspect-contact.c +++ b/examples/client/inspect-contact.c @@ -44,75 +44,62 @@ display_contact (TpContact *contact) } static void -contacts_upgraded_cb (TpConnection *connection, - guint n_contacts, - TpContact * const *contacts, - const GError *error, - gpointer user_data, - GObject *weak_object) +contacts_upgraded_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) { + TpConnection *connection = (TpConnection *) object; InspectContactData *data = user_data; + GPtrArray *contacts; + GError *error = NULL; - if (error == NULL) + if (!tp_connection_upgrade_contacts_finish (connection, result, + &contacts, &error)) + { + g_warning ("Error getting contacts: %s", error->message); + data->exit_status = 1; + g_clear_error (&error); + } + else { guint i; data->exit_status = 0; - for (i = 0; i < n_contacts; i++) + for (i = 0; i < contacts->len; i++) { - display_contact (contacts[i]); + display_contact (g_ptr_array_index (contacts, i)); } - } - else - { - g_warning ("Error getting contacts: %s", error->message); - data->exit_status = 1; + g_ptr_array_unref (contacts); } g_main_loop_quit (data->main_loop); } static void -got_contacts_by_id (TpConnection *connection, - guint n_contacts, - TpContact * const *contacts, - const gchar * const *good_ids, - GHashTable *bad_ids, - const GError *error, - gpointer user_data, - GObject *weak_object) +got_contacts_by_id (GObject *object, + GAsyncResult *result, + gpointer user_data) { + TpConnection *connection = (TpConnection *) object; InspectContactData *data = user_data; + TpContact *contact; + GError *error = NULL; - if (error == NULL) - { - guint i; - GHashTableIter hash_iter; - gpointer key, value; - - data->exit_status = 0; - - for (i = 0; i < n_contacts; i++) - { - display_contact (contacts[i]); - } - - g_hash_table_iter_init (&hash_iter, bad_ids); - - while (g_hash_table_iter_next (&hash_iter, &key, &value)) - { - gchar *id = key; - GError *e = value; + contact = tp_connection_dup_contact_by_id_finish (connection, result, &error); - g_warning ("Invalid ID \"%s\": %s", id, e->message); - data->exit_status = 1; - } - } - else + if (contact == NULL) { g_warning ("Error getting contacts: %s", error->message); data->exit_status = 1; + g_clear_error (&error); + } + else + { + data->exit_status = 0; + + display_contact (contact); + g_object_unref (contact); } g_main_loop_quit (data->main_loop); @@ -146,21 +133,19 @@ connection_ready_cb (GObject *source, { TpContact *self_contact = tp_connection_get_self_contact (connection); - tp_connection_upgrade_contacts (connection, + tp_connection_upgrade_contacts_async (connection, 1, &self_contact, features, contacts_upgraded_cb, - data, NULL, NULL); + data); } else { - const gchar *contacts[] = { data->to_inspect, NULL }; - - tp_connection_get_contacts_by_id (connection, - 1, contacts, + tp_connection_dup_contact_by_id_async (connection, + data->to_inspect, features, got_contacts_by_id, - data, NULL, NULL); + data); } } diff --git a/examples/cm/echo-message-parts/chan.c b/examples/cm/echo-message-parts/chan.c index 7c1d8d450..47325793b 100644 --- a/examples/cm/echo-message-parts/chan.c +++ b/examples/cm/echo-message-parts/chan.c @@ -29,6 +29,8 @@ G_DEFINE_TYPE_WITH_CODE (ExampleEcho2Channel, TP_TYPE_BASE_CHANNEL, G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_TYPE_TEXT, tp_message_mixin_iface_init) + G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_CHAT_STATE, + tp_message_mixin_chat_state_iface_init) G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_DESTROYABLE, destroyable_iface_init) G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_SMS, sms_iface_init) @@ -46,6 +48,8 @@ example_echo_2_channel_get_interfaces (TpBaseChannel *self) g_ptr_array_add (interfaces, TP_IFACE_CHANNEL_INTERFACE_DESTROYABLE); g_ptr_array_add (interfaces, TP_IFACE_CHANNEL_INTERFACE_SMS); + g_ptr_array_add (interfaces, TP_IFACE_CHANNEL_INTERFACE_CHAT_STATE); + return interfaces; }; @@ -173,6 +177,13 @@ finally: } } +static gboolean +send_chat_state (GObject *object, + TpChannelChatState state, + GError **error) +{ + return TRUE; +} static GObject * constructor (GType type, @@ -203,6 +214,8 @@ constructor (GType type, TP_DELIVERY_REPORTING_SUPPORT_FLAG_RECEIVE_FAILURES, content_types); + tp_message_mixin_implement_send_chat_state (object, send_chat_state); + return object; } @@ -219,6 +232,8 @@ example_echo_2_channel_close (TpBaseChannel *self) { GObject *object = (GObject *) self; + tp_message_mixin_maybe_send_gone (object); + if (!tp_base_channel_is_destroyed (self)) { TpHandle first_sender; diff --git a/spec/Channel_Type_Call1.xml b/spec/Channel_Type_Call1.xml index 548b79ce4..6809ff269 100644 --- a/spec/Channel_Type_Call1.xml +++ b/spec/Channel_Type_Call1.xml @@ -917,13 +917,10 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. <p>The change was requested by the contact indicated by the Actor member of a <tp:type>Call_State_Reason</tp:type> struct.</p> - <p>If the Actor is the local user, the DBus_Reason SHOULD be the - empty string.</p> - - <p>If the Actor is a remote user, the DBus_Reason SHOULD be the empty - string if the call was terminated normally, but MAY be a non-empty - error name to indicate error-like call termination reasons (call - rejected as busy, kicked from a conference by a moderator, etc.).</p> + <p>The DBus_Reason SHOULD be the empty string if the call + was terminated normally, but MAY be a non-empty error name + to indicate error-like call termination reasons (kicked from + a conference by a moderator, etc.).</p> </tp:docstring> </tp:enumvalue> diff --git a/spec/Connection_Interface_Contacts.xml b/spec/Connection_Interface_Contacts.xml index 1a16d2468..c2be7ff9d 100644 --- a/spec/Connection_Interface_Contacts.xml +++ b/spec/Connection_Interface_Contacts.xml @@ -171,6 +171,64 @@ <tp:error name="im.telepathy1.Error.Disconnected"/> </tp:possible-errors> </method> + + <method name="GetContactByID" + tp:name-for-bindings="Get_Contact_By_ID"> + <tp:added version="0.27.0"/> + <tp:docstring> + Return any number of contact attributes for the given identifier. + <tp:rationale> + This is for a single identifier to make it simpler to use for the most + common use case. For multiple contacts case, + <tp:member-ref>GetContactAttributes</tp:member-ref> should be used. + </tp:rationale> + </tp:docstring> + + <arg direction="in" name="Identifier" type="s"> + <tp:docstring> + An identifier representing a contact. + </tp:docstring> + </arg> + + <arg direction="in" name="Interfaces" type="as" + tp:type="DBus_Interface[]"> + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + <p>A list of strings indicating which D-Bus interfaces the calling + process is interested in. All supported attributes from these + interfaces, whose values can be obtained without additional network + activity, will be in the reply.</p> + <p>See <tp:member-ref>GetContactAttributes</tp:member-ref> for + details.</p> + </tp:docstring> + </arg> + + <arg direction="out" name="Handle" type="u" tp:type="Contact_Handle"> + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + <p>The contact's handle, as returned by <tp:dbus-ref + namespace="ofdT.Connection">RequestHandles</tp:dbus-ref></p> + </tp:docstring> + </arg> + + <arg direction="out" type="a{sv}" name="Attributes" + tp:type="Single_Contact_Attributes_Map"> + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + <p>All supported attributes of the contact on + the given interfaces that can be returned without network + round-trips. If contact attributes are not immediately known, the + behaviour is defined by the interface; the attribute should either + be omitted from the result or replaced with a default value.</p> + + <p>The contact's attributes will always include at least the + identifier that would be obtained by inspecting the handle + (<code>org.freedesktop.Telepathy.Connection/contact-id</code>).</p> + </tp:docstring> + </arg> + + <tp:possible-errors> + <tp:error name="org.freedesktop.Telepathy.Error.Disconnected"/> + <tp:error name="org.freedesktop.Telepathy.Error.InvalidHandle"/> + </tp:possible-errors> + </method> </interface> </node> <!-- vim:set sw=2 sts=2 et ft=xml: --> diff --git a/spec/Connection_Manager.xml b/spec/Connection_Manager.xml index abcdae7db..090908a31 100644 --- a/spec/Connection_Manager.xml +++ b/spec/Connection_Manager.xml @@ -216,7 +216,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.</ </tp:mapping> <property name="Protocols" tp:name-for-bindings="Protocols" - access="read" type="a{sa{sv}}" tp:type="Protocol_Properties_Map"> + access="read" type="a{sa{sv}}" tp:type="Protocol_Properties_Map" + tp:immutable="yes"> <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> <p>A map from protocol identifiers supported by this connection manager to the immutable properties of the corresponding diff --git a/spec/Protocol_Interface_Presence1.xml b/spec/Protocol_Interface_Presence1.xml index 9ae79e5ca..a0cc12588 100644 --- a/spec/Protocol_Interface_Presence1.xml +++ b/spec/Protocol_Interface_Presence1.xml @@ -96,7 +96,8 @@ status-chat=2 settable message <property name="Statuses" tp:name-for-bindings="Statuses" - type="a{s(ubb)}" tp:type="Status_Spec_Map" access="read"> + type="a{s(ubb)}" tp:type="Status_Spec_Map" access="read" + tp:immutable="yes"> <tp:docstring> <p>The statuses that might appear in the <tp:dbus-ref namespace="im.telepathy1" diff --git a/spec/all.xml b/spec/all.xml index a792a09fd..8e903f59e 100644 --- a/spec/all.xml +++ b/spec/all.xml @@ -3,7 +3,7 @@ xmlns:xi="http://www.w3.org/2001/XInclude"> <tp:title>Telepathy D-Bus Interface Specification</tp:title> -<tp:version>0.25.2.1</tp:version> +<tp:version>0.27.0</tp:version> <tp:copyright>Copyright © 2005-2012 Collabora Limited</tp:copyright> <tp:copyright>Copyright © 2005-2011 Nokia Corporation</tp:copyright> @@ -141,7 +141,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.</ <xi:include href="Channel_Type_Text.xml"/> </tp:section> - <tp:section name="Channel Interfaces"> + <tp:section name="Channel interfaces"> <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> <p> A Channel may also implement one or more of the following interfaces, @@ -217,6 +217,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.</ </tp:section> <tp:section name="Calls"> +<<<<<<< HEAD <xi:include href="Call1_Content.xml"/> <xi:include href="Call1_Content_Interface_Media.xml"/> <xi:include href="Call1_Interface_Mute.xml"/> @@ -231,6 +232,34 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.</ <xi:include href="Call1_Stream.xml"/> <xi:include href="Call1_Stream_Interface_Media.xml"/> <xi:include href="Call1_Stream_Endpoint.xml"/> +======= + <xi:include href="Call_Content.xml"/> + <xi:include href="Call_Content_Interface_DTMF.xml"/> + <xi:include href="Call_Stream.xml"/> + <xi:include href="Call_Interface_Mute.xml"/> + </tp:section> + + <tp:section name="Call media interfaces"> + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + <p> + These interfaces are used when a <tp:dbus-ref + namespace='ofdT.Channel.Type'>Call1</tp:dbus-ref> channel + doesn't have <tp:dbus-ref + namespace="ofdT.Channel.Type.Call1">HardwareStreaming</tp:dbus-ref> to + implement the media streaming aspects of a call. + </p> + </tp:docstring> + <xi:include href="Call_Content_Interface_Media.xml"/> + <xi:include href="Call_Content_Interface_Video_Control.xml"/> + <xi:include href="Call_Content_Interface_Audio_Control.xml"/> + <xi:include href="Call_Content_Media_Description.xml"/> + <xi:include href="Call_Content_Media_Description_Interface_RTP_Header_Extensions.xml"/> + <xi:include href="Call_Content_Media_Description_Interface_RTCP_Feedback.xml"/> + <xi:include + href="Call_Content_Media_Description_Interface_RTCP_Extended_Reports.xml"/> + <xi:include href="Call_Stream_Interface_Media.xml"/> + <xi:include href="Call_Stream_Endpoint.xml"/> +>>>>>>> master </tp:section> <tp:section name="Debugging"> diff --git a/telepathy-glib/Makefile.am b/telepathy-glib/Makefile.am index bf0f9338f..aeba99057 100644 --- a/telepathy-glib/Makefile.am +++ b/telepathy-glib/Makefile.am @@ -126,6 +126,8 @@ tpginclude_HEADERS = \ stream-tube-connection.h \ telepathy-glib.h \ text-channel.h \ + tls-certificate.h \ + tls-certificate-rejection.h \ util.h \ $(NULL) @@ -328,6 +330,9 @@ libtelepathy_glib_main_internal_la_SOURCES = \ stream-tube-connection-internal.h \ stream-tube-connection.c \ text-channel.c \ + tls-certificate.c \ + tls-certificate-rejection.c \ + tls-certificate-rejection-internal.h \ util.c \ util-internal.h diff --git a/telepathy-glib/account-channel-request.c b/telepathy-glib/account-channel-request.c index a3128fa81..fbc4bd00f 100644 --- a/telepathy-glib/account-channel-request.c +++ b/telepathy-glib/account-channel-request.c @@ -1634,7 +1634,7 @@ _tp_account_channel_request_get_client (TpAccountChannelRequest *self) * This function can't be called once @self has been used to request a * channel. * - * Since: 0.19.UNRELEASED + * Since: 0.19.0 */ void tp_account_channel_request_set_target_contact ( @@ -1669,7 +1669,7 @@ tp_account_channel_request_set_target_contact ( * This function can't be called once @self has been used to request a * channel. * - * Since: 0.19.UNRELEASED + * Since: 0.19.0 */ void tp_account_channel_request_set_target_id ( @@ -1710,7 +1710,7 @@ tp_account_channel_request_set_target_id ( * * Returns: a new #TpAccountChannelRequest object * - * Since: 0.19.UNRELEASED + * Since: 0.19.0 */ TpAccountChannelRequest * tp_account_channel_request_new_text ( @@ -1769,7 +1769,7 @@ tp_account_channel_request_new_text ( * This function can't be called once @self has been used to request a * channel. * - * Since: 0.19.UNRELEASED + * Since: 0.19.0 */ void tp_account_channel_request_set_request_property ( @@ -1828,7 +1828,7 @@ tp_account_channel_request_set_request_property ( * * Returns: a new #TpAccountChannelRequest object * - * Since: 0.19.UNRELEASED + * Since: 0.19.0 */ TpAccountChannelRequest * tp_account_channel_request_new_audio_call ( @@ -1877,7 +1877,7 @@ tp_account_channel_request_new_audio_call ( * * Returns: a new #TpAccountChannelRequest object * - * Since: 0.19.UNRELEASED + * Since: 0.19.0 */ TpAccountChannelRequest * tp_account_channel_request_new_audio_video_call ( @@ -1930,7 +1930,7 @@ tp_account_channel_request_new_audio_video_call ( * * Returns: a new #TpAccountChannelRequest object * - * Since: 0.19.UNRELEASED + * Since: 0.19.0 */ TpAccountChannelRequest * tp_account_channel_request_new_file_transfer ( @@ -1984,7 +1984,7 @@ tp_account_channel_request_new_file_transfer ( * This function can't be called once @self has been used to request a * channel. * - * Since: 0.19.UNRELEASED + * Since: 0.19.0 */ void tp_account_channel_request_set_file_transfer_description ( @@ -2032,7 +2032,7 @@ tp_account_channel_request_set_file_transfer_description ( * This function can't be called once @self has been used to request a * channel. * - * Since: 0.19.UNRELEASED + * Since: 0.19.0 */ void tp_account_channel_request_set_file_transfer_uri ( @@ -2067,7 +2067,7 @@ tp_account_channel_request_set_file_transfer_uri ( * This function can't be called once @self has been used to request a * channel. * - * Since: 0.19.UNRELEASED + * Since: 0.19.0 */ void tp_account_channel_request_set_file_transfer_timestamp ( @@ -2103,7 +2103,7 @@ tp_account_channel_request_set_file_transfer_timestamp ( * This function can't be called once @self has been used to request a * channel. * - * Since: 0.19.UNRELEASED + * Since: 0.19.0 */ void tp_account_channel_request_set_file_transfer_initial_offset ( diff --git a/telepathy-glib/account.c b/telepathy-glib/account.c index f085c9deb..466ecc3e0 100644 --- a/telepathy-glib/account.c +++ b/telepathy-glib/account.c @@ -465,6 +465,18 @@ OUT: g_object_unref (self); } +static void _tp_account_set_connection (TpAccount *account, const gchar *path); + +static void +connection_invalidated_cb (TpConnection *connection, + guint domain, + gint code, + gchar *message, + TpAccount *account) +{ + _tp_account_set_connection (account, "/"); +} + static void _tp_account_set_connection (TpAccount *account, const gchar *path) @@ -474,14 +486,17 @@ _tp_account_set_connection (TpAccount *account, gboolean have_public_connection; GError *error = NULL; - /* Do nothing if we already have a connection for the same path */ if (priv->connection != NULL) { const gchar *current; + /* Do nothing if we already have a connection for the same path */ current = tp_proxy_get_object_path (priv->connection); if (!tp_strdiff (current, path)) return; + + g_signal_handlers_disconnect_by_func (priv->connection, + connection_invalidated_cb, account); } had_public_connection = (priv->connection != NULL && @@ -514,6 +529,9 @@ _tp_account_set_connection (TpAccount *account, } else { + tp_g_signal_connect_object (priv->connection, "invalidated", + G_CALLBACK (connection_invalidated_cb), account, 0); + _tp_connection_set_account (priv->connection, account); if (tp_proxy_is_prepared (account, TP_ACCOUNT_FEATURE_CONNECTION)) { @@ -1162,7 +1180,7 @@ _tp_account_dispose (GObject *object) priv->dispose_has_run = TRUE; - tp_clear_object (&self->priv->connection); + _tp_account_set_connection (self, "/"); /* release any references held by the object here */ if (G_OBJECT_CLASS (tp_account_parent_class)->dispose != NULL) @@ -2777,20 +2795,16 @@ tp_account_update_parameters_vardict_async (TpAccount *account, GAsyncReadyCallback callback, gpointer user_data) { - GValue v = G_VALUE_INIT; + GHashTable *hash; - g_return_if_fail (parameters != NULL); - g_return_if_fail (g_variant_is_of_type (parameters, G_VARIANT_TYPE_VARDICT)); + hash = _tp_asv_from_vardict (parameters); g_variant_ref_sink (parameters); - dbus_g_value_parse_g_variant (parameters, &v); - g_assert (G_VALUE_HOLDS (&v, TP_HASH_TYPE_STRING_VARIANT_MAP)); - - tp_account_update_parameters_async (account, g_value_get_boxed (&v), + tp_account_update_parameters_async (account, hash, unset_parameters, callback, user_data); - g_value_unset (&v); g_variant_unref (parameters); + g_hash_table_unref (hash); } /** diff --git a/telepathy-glib/base-connection.c b/telepathy-glib/base-connection.c index 8bb894e0c..e7d15c2b0 100644 --- a/telepathy-glib/base-connection.c +++ b/telepathy-glib/base-connection.c @@ -227,6 +227,7 @@ #include <telepathy-glib/contacts-mixin.h> #include <telepathy-glib/dbus-properties-mixin.h> #include <telepathy-glib/dbus.h> +#include <telepathy-glib/dbus-internal.h> #include <telepathy-glib/exportable-channel.h> #include <telepathy-glib/gtypes.h> #include <telepathy-glib/interfaces.h> @@ -1893,29 +1894,25 @@ tp_base_connection_disconnect_with_dbus_error_vardict (TpBaseConnection *self, GVariant *details, TpConnectionStatusReason reason) { - GValue value = G_VALUE_INIT; + GHashTable *hash; g_return_if_fail (TP_IS_BASE_CONNECTION (self)); g_return_if_fail (tp_dbus_check_valid_interface_name (error_name, NULL)); - g_return_if_fail (g_variant_is_of_type (details, G_VARIANT_TYPE_VARDICT)); if (details == NULL) { - g_value_init (&value, TP_HASH_TYPE_STRING_VARIANT_MAP); - g_value_take_boxed (&value, g_hash_table_new (g_str_hash, g_str_equal)); + hash = g_hash_table_new (g_str_hash, g_str_equal); } else { - dbus_g_value_parse_g_variant (details, &value); - g_assert (G_VALUE_TYPE (&value) == TP_HASH_TYPE_STRING_VARIANT_MAP); + hash = _tp_asv_from_vardict (details); } - tp_svc_connection_emit_connection_error (self, error_name, - g_value_get_boxed (&value)); + tp_svc_connection_emit_connection_error (self, error_name, hash); tp_base_connection_change_status (self, TP_CONNECTION_STATUS_DISCONNECTED, reason); - g_value_unset (&value); + g_hash_table_unref (hash); } /** diff --git a/telepathy-glib/capabilities.c b/telepathy-glib/capabilities.c index 4913ceee6..633617da4 100644 --- a/telepathy-glib/capabilities.c +++ b/telepathy-glib/capabilities.c @@ -264,7 +264,7 @@ tp_capabilities_class_init (TpCapabilitiesClass *klass) * tp_capabilities_supports_text_chats() are likely to be more useful to * the majority of clients. * - * Since: UNRELEASED + * Since: 0.19.0 */ param_spec = g_param_spec_variant ("channel-classes-variant", "GVariant of type a(a{sv}as)", @@ -419,7 +419,7 @@ tp_capabilities_supports_text_chatrooms (TpCapabilities *self) * #TP_PROP_CHANNEL_INTERFACE_SMS_SMS_CHANNEL set to %TRUE can be * expected to work, %FALSE otherwise. * - * Since: 0.UNRELEASED + * Since: 0.19.0 */ gboolean tp_capabilities_supports_sms (TpCapabilities *self) @@ -727,7 +727,7 @@ tp_capabilities_supports_file_transfer (TpCapabilities *self) * tp_capabilities_supports_file_transfer() can also specify the outgoing * file's URI * - * Since: 0.19.UNRELEASED + * Since: 0.19.0 */ gboolean tp_capabilities_supports_file_transfer_uri (TpCapabilities *self) @@ -745,7 +745,7 @@ tp_capabilities_supports_file_transfer_uri (TpCapabilities *self) * tp_capabilities_supports_file_transfer() can also specify the outgoing * file's description * - * Since: 0.19.UNRELEASED + * Since: 0.19.0 */ gboolean tp_capabilities_supports_file_transfer_description (TpCapabilities *self) @@ -765,7 +765,7 @@ tp_capabilities_supports_file_transfer_description (TpCapabilities *self) * tp_capabilities_supports_file_transfer() can also specify an * initial offset greater than 0 * - * Since: 0.19.UNRELEASED + * Since: 0.19.0 */ gboolean tp_capabilities_supports_file_transfer_initial_offset (TpCapabilities *self) @@ -783,7 +783,7 @@ tp_capabilities_supports_file_transfer_initial_offset (TpCapabilities *self) * tp_capabilities_supports_file_transfer() can also specify the outgoing * file's timestamp * - * Since: 0.19.UNRELEASED + * Since: 0.19.0 */ gboolean tp_capabilities_supports_file_transfer_timestamp (TpCapabilities *self) @@ -1085,7 +1085,7 @@ tp_capabilities_supports_room_list (TpCapabilities *self, * Returns: (transfer full): the value of the * #TpCapabilities:channel-classes-variant property * - * Since: UNRELEASED + * Since: 0.19.0 */ GVariant * tp_capabilities_dup_channel_classes_variant (TpCapabilities *self) diff --git a/telepathy-glib/channel-contacts.c b/telepathy-glib/channel-contacts.c index 1c79237e8..3e8cd50c5 100644 --- a/telepathy-glib/channel-contacts.c +++ b/telepathy-glib/channel-contacts.c @@ -341,6 +341,10 @@ process_contacts_queue (TpChannel *self) features = tp_client_factory_dup_contact_features ( tp_proxy_get_factory (self->priv->connection), self->priv->connection); + /* We can't use upgrade_contacts_async() because we need compat with older + * CMs. by_id and by_handle are used only by TpTextChannel and are needed for + * older CMs that does not give both message-sender and message-sender-id */ + G_GNUC_BEGIN_IGNORE_DEPRECATIONS if (item->contacts != NULL && item->contacts->len > 0) { g_assert (item->ids == NULL); @@ -385,6 +389,7 @@ process_contacts_queue (TpChannel *self) * without reentering mainloop first. */ g_idle_add (contacts_queue_item_idle_cb, self); } + G_GNUC_END_IGNORE_DEPRECATIONS g_array_unref (features); } @@ -600,7 +605,8 @@ _tp_channel_contacts_members_changed (TpChannel *self, ids = tp_asv_get_boxed (details, "contact-ids", TP_HASH_TYPE_HANDLE_IDENTIFIER_MAP); - if (ids == NULL) + if (ids == NULL && (added->len > 0 || local_pending->len > 0 || + remote_pending->len > 0 || actor != 0 )) { DEBUG ("CM did not give identifiers, can't create TpContact"); return; diff --git a/telepathy-glib/channel.c b/telepathy-glib/channel.c index 97af2acaa..faa5d2d5d 100644 --- a/telepathy-glib/channel.c +++ b/telepathy-glib/channel.c @@ -210,6 +210,7 @@ tp_channel_get_feature_quark_contacts (void) * tp_proxy_prepare_async() function, and waiting for it to callback. * * Since: 0.11.3 + * Deprecated: Use TP_TEXT_CHANNEL_FEATURE_CHAT_STATES instead. */ GQuark @@ -490,6 +491,7 @@ tp_channel_get_property (GObject *object, * Returns: the chat state for @contact, or %TP_CHANNEL_CHAT_STATE_INACTIVE * if their chat state is not known * Since: 0.11.3 + * Deprecated: Use tp_text_channel_get_chat_state() instead. */ TpChannelChatState tp_channel_get_chat_state (TpChannel *self, @@ -696,9 +698,11 @@ tp_channel_chat_state_changed_cb (TpChannel *self, g_hash_table_insert (self->priv->chat_states, GUINT_TO_POINTER (contact), GUINT_TO_POINTER (state)); + G_GNUC_BEGIN_IGNORE_DEPRECATIONS /* Don't emit the signal until we've had the initial state */ if (!tp_proxy_is_prepared (self, TP_CHANNEL_FEATURE_CHAT_STATES)) return; + G_GNUC_END_IGNORE_DEPRECATIONS g_signal_emit (self, signals[SIGNAL_CHAT_STATE_CHANGED], 0, contact, state); } @@ -905,19 +909,19 @@ _tp_channel_prepare_connection (TpChannel *self) } static void -upgrade_contacts_cb (TpConnection *connection, - guint n_contacts, - TpContact * const *contacts, - const GError *error, - gpointer user_data, - GObject *weak_object) +upgrade_contacts_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) { - TpChannel *self = (TpChannel *) weak_object; + TpChannel *self = user_data; + TpConnection *connection = (TpConnection *) object; + GError *error = NULL; - if (error != NULL) + if (!tp_connection_upgrade_contacts_finish (connection, result, NULL, &error)) { _tp_channel_abort_introspection (self, "Upgrading contacts failed", error); + g_clear_error (&error); return; } @@ -985,10 +989,10 @@ _tp_channel_create_contacts (TpChannel *self) tp_proxy_get_factory (self->priv->connection), self->priv->connection); - tp_connection_upgrade_contacts (self->priv->connection, + tp_connection_upgrade_contacts_async (self->priv->connection, contacts->len, (TpContact **) contacts->pdata, (GQuark *) features->data, - upgrade_contacts_cb, NULL, NULL, (GObject *) self); + upgrade_contacts_cb, self); g_array_unref (features); } @@ -1275,11 +1279,13 @@ tp_channel_list_features (TpProxyClass *cls G_GNUC_UNUSED) features[FEAT_CONTACTS].prepare_async = _tp_channel_contacts_prepare_async; + G_GNUC_BEGIN_IGNORE_DEPRECATIONS features[FEAT_CHAT_STATES].name = TP_CHANNEL_FEATURE_CHAT_STATES; features[FEAT_CHAT_STATES].prepare_async = tp_channel_prepare_chat_states_async; need_chat_states[0] = TP_IFACE_QUARK_CHANNEL_INTERFACE_CHAT_STATE; features[FEAT_CHAT_STATES].interfaces_needed = need_chat_states; + G_GNUC_END_IGNORE_DEPRECATIONS features[FEAT_PASSWORD].name = TP_CHANNEL_FEATURE_PASSWORD; features[FEAT_PASSWORD].prepare_async = @@ -1526,6 +1532,7 @@ tp_channel_class_init (TpChannelClass *klass) * has finished preparing the feature %TP_CHANNEL_FEATURE_CHAT_STATES. * * Since: 0.11.3 + * Deprecated: Use #TpTextChannel::contact-chat-state-changed instead */ signals[SIGNAL_CHAT_STATE_CHANGED] = g_signal_new ("chat-state-changed", G_OBJECT_CLASS_TYPE (klass), diff --git a/telepathy-glib/channel.h b/telepathy-glib/channel.h index 470c96620..73da1abe7 100644 --- a/telepathy-glib/channel.h +++ b/telepathy-glib/channel.h @@ -194,7 +194,10 @@ TpContact *tp_channel_group_get_contact_owner (TpChannel *self, #define TP_CHANNEL_FEATURE_CHAT_STATES \ tp_channel_get_feature_quark_chat_states () +_TP_DEPRECATED_IN_0_20_FOR(tp_text_channel_get_feature_quark_chat_states) GQuark tp_channel_get_feature_quark_chat_states (void) G_GNUC_CONST; + +_TP_DEPRECATED_IN_0_20_FOR(tp_text_channel_get_chat_state) TpChannelChatState tp_channel_get_chat_state (TpChannel *self, TpHandle contact); diff --git a/telepathy-glib/cli-misc.h b/telepathy-glib/cli-misc.h index 1dc079b8f..7c8bc9b63 100644 --- a/telepathy-glib/cli-misc.h +++ b/telepathy-glib/cli-misc.h @@ -32,6 +32,7 @@ #include <telepathy-glib/debug-client.h> #include <telepathy-glib/protocol.h> #include <telepathy-glib/proxy.h> +#include <telepathy-glib/tls-certificate.h> #include <telepathy-glib/_gen/tp-cli-account.h> #include <telepathy-glib/_gen/tp-cli-account-manager.h> @@ -44,6 +45,7 @@ #include <telepathy-glib/_gen/tp-cli-debug.h> #include <telepathy-glib/_gen/tp-cli-generic.h> #include <telepathy-glib/_gen/tp-cli-protocol.h> +#include <telepathy-glib/_gen/tp-cli-tls-cert.h> #endif diff --git a/telepathy-glib/codegen.am b/telepathy-glib/codegen.am index a7668b8ad..4135d16e4 100644 --- a/telepathy-glib/codegen.am +++ b/telepathy-glib/codegen.am @@ -66,6 +66,7 @@ nodist_gendbusinclude_HEADERS += \ _gen/tp-cli-debug.h \ _gen/tp-cli-generic.h \ _gen/tp-cli-protocol.h \ + _gen/tp-cli-tls-cert.h \ _gen/tp-svc-account.h \ _gen/tp-svc-account-manager.h \ _gen/tp-svc-call-content.h \ @@ -117,6 +118,7 @@ nodist_libtelepathy_glib_dbus_internal_la_SOURCES += \ _gen/tp-cli-debug-body.h \ _gen/tp-cli-generic-body.h \ _gen/tp-cli-protocol-body.h \ + _gen/tp-cli-tls-cert-body.h \ _gen/tp-svc-account.c \ _gen/tp-svc-account-manager.c \ _gen/tp-svc-call-content.c \ @@ -319,6 +321,10 @@ _gen/tp-cli-%-body.h: _gen/tp-spec-%.xml \ subclass=--subclass=TpDebugClient; \ subclass_assert=--subclass-assert=TP_IS_DEBUG_CLIENT; \ ;; \ + tls-cert) \ + subclass=--subclass=TpTLSCertificate; \ + subclass_assert=--subclass-assert=TP_IS_TLS_CERTIFICATE; \ + ;; \ esac; \ $(PYTHON) $(tools_dir)/glib-client-gen.py \ $$subclass $$subclass_assert \ diff --git a/telepathy-glib/connection-contact-list.c b/telepathy-glib/connection-contact-list.c index 965632beb..4cd8fcb14 100644 --- a/telepathy-glib/connection-contact-list.c +++ b/telepathy-glib/connection-contact-list.c @@ -136,16 +136,17 @@ contacts_changed_head_ready (TpConnection *self) } static void -new_contacts_upgraded_cb (TpConnection *self, - guint n_contacts, - TpContact * const *contacts, - const GError *error, - gpointer user_data, - GObject *weak_object) +new_contacts_upgraded_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) { - if (error != NULL) + TpConnection *self = (TpConnection *) object; + GError *error = NULL; + + if (!tp_connection_upgrade_contacts_finish (self, result, NULL, &error)) { DEBUG ("Error upgrading new roster contacts: %s", error->message); + g_clear_error (&error); } contacts_changed_head_ready (self); @@ -191,10 +192,10 @@ process_queued_contacts_changed (TpConnection *self) features = tp_client_factory_dup_contact_features ( tp_proxy_get_factory (self), self); - tp_connection_upgrade_contacts (self, + tp_connection_upgrade_contacts_async (self, item->new_contacts->len, (TpContact **) item->new_contacts->pdata, (const GQuark *) features->data, - new_contacts_upgraded_cb, NULL, NULL, NULL); + new_contacts_upgraded_cb, NULL); g_array_unref (features); } @@ -1657,31 +1658,32 @@ blocked_changed_head_ready (TpConnection *self) } static void -blocked_contacts_upgraded_cb (TpConnection *self, - guint n_contacts, - TpContact * const *contacts, - const GError *error, - gpointer user_data, - GObject *weak_object) +blocked_contacts_upgraded_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) { + TpConnection *self = (TpConnection *) object; BlockedChangedItem *item; guint i; GPtrArray *added, *removed; + GPtrArray *contacts; + GError *error = NULL; item = g_queue_peek_head (self->priv->blocked_changed_queue); - if (error != NULL) + if (!tp_connection_upgrade_contacts_finish (self, result, &contacts, &error)) { DEBUG ("Error upgrading blocked contacts: %s", error->message); + g_clear_error (&error); goto out; } added = g_ptr_array_new (); removed = g_ptr_array_new_with_free_func (g_object_unref); - for (i = 0; i < n_contacts; i++) + for (i = 0; i < contacts->len; i++) { - TpContact *contact = contacts[i]; + TpContact *contact = g_ptr_array_index (contacts, i); TpHandle handle; handle = tp_contact_get_handle (contact); @@ -1720,6 +1722,7 @@ blocked_contacts_upgraded_cb (TpConnection *self, g_ptr_array_unref (added); g_ptr_array_unref (removed); + g_ptr_array_unref (contacts); out: blocked_changed_head_ready (self); @@ -1784,10 +1787,10 @@ process_queued_blocked_changed (TpConnection *self) features = tp_client_factory_dup_contact_features ( tp_proxy_get_factory (self), self); - tp_connection_upgrade_contacts (self, + tp_connection_upgrade_contacts_async (self, contacts->len, (TpContact **) contacts->pdata, (const GQuark *) features->data, - blocked_contacts_upgraded_cb, NULL, NULL, NULL); + blocked_contacts_upgraded_cb, NULL); g_array_unref (features); g_ptr_array_unref (contacts); diff --git a/telepathy-glib/connection-manager.c b/telepathy-glib/connection-manager.c index 375dddea5..0a41eaee3 100644 --- a/telepathy-glib/connection-manager.c +++ b/telepathy-glib/connection-manager.c @@ -2255,7 +2255,7 @@ tp_connection_manager_param_get_default ( * %G_VARIANT_TYPE_STRING. * * Returns: the default value, or %NULL if there is no default - * Since: 0.19.UNRELEASED + * Since: 0.19.0 */ GVariant * tp_connection_manager_param_dup_default_variant ( diff --git a/telepathy-glib/connection.c b/telepathy-glib/connection.c index 59e1470f5..6edc921ef 100644 --- a/telepathy-glib/connection.c +++ b/telepathy-glib/connection.c @@ -855,10 +855,12 @@ get_self_contact (TpConnection *self) * require immortal-handles and spec change to give the self identifier. */ /* This relies on the special case in tp_connection_get_contacts_by_handle() * which makes it start working slightly early. */ + G_GNUC_BEGIN_IGNORE_DEPRECATIONS tp_connection_get_contacts_by_handle (self, 1, &self->priv->last_known_self_handle, (GQuark *) features->data, tp_connection_got_self_contact_cb, NULL, NULL, NULL); + G_GNUC_END_IGNORE_DEPRECATIONS g_array_unref (features); } @@ -1292,7 +1294,10 @@ tp_connection_constructed (GObject *object) tp_cli_dbus_properties_call_get_all (self, -1, TP_IFACE_CONNECTION, _tp_connection_got_properties, NULL, NULL, NULL); - g_signal_connect (self, "invalidated", + /* Give a chance to TpAccount to know about invalidated connection before we + * unref all roster contacts. This is to let applications properly remove all + * contacts at once instead of getting weak notify on each. */ + g_signal_connect_after (self, "invalidated", G_CALLBACK (tp_connection_invalidated), NULL); } @@ -2830,7 +2835,7 @@ tp_connection_get_detailed_error (TpConnection *self, * * Returns: (transfer full) (allow-none): a D-Bus error name, or %NULL. * - * Since: 0.19.UNRELEASED + * Since: 0.19.0 */ gchar * tp_connection_dup_detailed_error_vardict (TpConnection *self, diff --git a/telepathy-glib/contact.c b/telepathy-glib/contact.c index 2352dfaee..42ffa9369 100644 --- a/telepathy-glib/contact.c +++ b/telepathy-glib/contact.c @@ -116,7 +116,7 @@ tp_contact_get_feature_quark_alias (void) * retrieved. In particular, the #TpContact:avatar-token property has * been set. * - * Since: 0.UNRELEASED + * Since: 0.19.0 */ GQuark @@ -419,7 +419,7 @@ struct _TpContactPrivate { * (it must be referenced with g_object_ref if it must remain valid * longer than the contact) * - * Since: 0.UNRELEASED + * Since: 0.19.0 */ TpAccount * tp_contact_get_account (TpContact *self) @@ -3606,6 +3606,7 @@ contacts_context_remove_common_features (ContactsContext *context) * connection managers. * * Since: 0.7.18 + * Deprecated: Use tp_client_factory_ensure_contact() instead. */ void tp_connection_get_contacts_by_handle (TpConnection *self, @@ -3686,6 +3687,7 @@ tp_connection_get_contacts_by_handle (TpConnection *self, * connection managers. * * Since: 0.7.18 + * Deprecated: Use tp_connection_upgrade_contacts_async() instead. */ void tp_connection_upgrade_contacts (TpConnection *self, @@ -3889,6 +3891,7 @@ contacts_requested_handles (TpConnection *connection, * connection managers. * * Since: 0.7.18 + * Deprecated: Use tp_connection_get_contact_by_id_async() instead. */ void tp_connection_get_contacts_by_id (TpConnection *self, @@ -3941,6 +3944,298 @@ tp_connection_get_contacts_by_id (TpConnection *self, weak_object); } +typedef struct +{ + GSimpleAsyncResult *result; + ContactFeatureFlags features; +} ContactsAsyncData; + +static ContactsAsyncData * +contacts_async_data_new (GSimpleAsyncResult *result, + ContactFeatureFlags features) +{ + ContactsAsyncData *data; + + data = g_slice_new0 (ContactsAsyncData); + data->result = g_object_ref (result); + data->features = features; + + return data; +} + +static void +contacts_async_data_free (ContactsAsyncData *data) +{ + g_object_unref (data->result); + g_slice_free (ContactsAsyncData, data); +} + +static void +got_contact_by_id_cb (TpConnection *self, + TpHandle handle, + GHashTable *attributes, + const GError *error, + gpointer user_data, + GObject *weak_object) +{ + ContactsAsyncData *data = user_data; + TpContact *contact; + GError *e = NULL; + + if (error != NULL) + { + g_simple_async_result_set_from_error (data->result, error); + g_simple_async_result_complete (data->result); + return; + } + + /* set up the contact with its attributes */ + contact = tp_contact_ensure (self, handle); + g_simple_async_result_set_op_res_gpointer (data->result, + contact, g_object_unref); + + if (!tp_contact_set_attributes (contact, attributes, data->features, &e)) + g_simple_async_result_take_error (data->result, e); + + g_simple_async_result_complete (data->result); +} + +/** + * tp_connection_dup_contact_by_id_async: + * @self: A connection, which must have the %TP_CONNECTION_FEATURE_CONNECTED + * feature prepared + * @id: A strings representing the desired contact by its + * identifier in the IM protocol (an XMPP JID, SIP URI, MSN Passport, + * AOL screen-name etc.) + * @features: (transfer-none) (array zero-terminated=1) (allow-none) + * (element-type GLib.Quark): An array of features that must be ready for + * @callback: A user callback to call when the contact is ready + * @user_data: Data to pass to the callback + * + * Create a #TpContact object and make any asynchronous method calls necessary + * to ensure that all the features specified in @features are ready for use + * (if they are supported at all). + * + * It is not an error to put features in @features even if the connection + * manager doesn't support them - users of this method should have a static + * list of features they would like to use if possible, and use it for all + * connection managers. + * + * Since: 0.19.0 + */ +void +tp_connection_dup_contact_by_id_async (TpConnection *self, + const gchar *id, + const GQuark *features, + GAsyncReadyCallback callback, + gpointer user_data) +{ + ContactsAsyncData *data; + GSimpleAsyncResult *result; + ContactFeatureFlags feature_flags = 0; + const gchar **supported_interfaces; + + g_return_if_fail (tp_proxy_is_prepared (self, + TP_CONNECTION_FEATURE_CONNECTED)); + g_return_if_fail (id != NULL); + + if (features == NULL) + features = no_quarks; + + if (!get_feature_flags (features, &feature_flags)) + return; + + supported_interfaces = contacts_bind_to_signals (self, feature_flags); + + result = g_simple_async_result_new ((GObject *) self, callback, user_data, + tp_connection_dup_contact_by_id_async); + + data = contacts_async_data_new (result, feature_flags); + tp_cli_connection_interface_contacts_call_get_contact_by_id (self, -1, + id, supported_interfaces, got_contact_by_id_cb, + data, (GDestroyNotify) contacts_async_data_free, NULL); + + g_free (supported_interfaces); + g_object_unref (result); +} + +/** + * tp_connection_dup_contact_by_id_finish: + * @self: a #TpConnection + * @result: a #GAsyncResult + * @error: a #GError to fill + * + * Finishes tp_connection_get_contact_by_id_async(). + * + * Returns: (transfer full): a #TpContact or %NULL on error. + * Since: 0.19.0 + */ +TpContact * +tp_connection_dup_contact_by_id_finish (TpConnection *self, + GAsyncResult *result, + GError **error) +{ + _tp_implement_finish_return_copy_pointer (self, + tp_connection_dup_contact_by_id_async, g_object_ref); +} + +static void +got_contact_attributes_cb (TpConnection *self, + GHashTable *attributes, + const GError *error, + gpointer user_data, + GObject *weak_object) +{ + ContactsAsyncData *data = user_data; + GHashTableIter iter; + gpointer key, value; + + if (error != NULL) + { + g_simple_async_result_set_from_error (data->result, error); + g_simple_async_result_complete (data->result); + return; + } + + g_hash_table_iter_init (&iter, attributes); + while (g_hash_table_iter_next (&iter, &key, &value)) + { + TpHandle handle = GPOINTER_TO_UINT (key); + GHashTable *asv = value; + TpContact *contact; + + /* set up the contact with its attributes */ + contact = tp_contact_ensure (self, handle); + tp_contact_set_attributes (contact, asv, data->features, NULL); + g_object_unref (contact); + } + + g_simple_async_result_complete (data->result); +} + +/** + * tp_connection_upgrade_contacts_async: + * @self: A connection, which must have the %TP_CONNECTION_FEATURE_CONNECTED + * feature prepared + * @n_contacts: The number of contacts in @contacts (must be at least 1) + * @contacts: (array length=n_contacts): An array of #TpContact objects + * associated with @self + * @features: (transfer-none) (array zero-terminated=1) (allow-none) + * (element-type GLib.Quark): An array of features that must be ready for + * @callback: A user callback to call when the contacts are ready + * @user_data: Data to pass to the callback + * + * Given several #TpContact objects, make asynchronous method calls + * ensure that all the features specified in @features are ready for use + * (if they are supported at all). + * + * It is not an error to put features in @features even if the connection + * manager doesn't support them - users of this method should have a static + * list of features they would like to use if possible, and use it for all + * connection managers. + * + * Since: 0.19.0 + */ +void +tp_connection_upgrade_contacts_async (TpConnection *self, + guint n_contacts, + TpContact * const *contacts, + const GQuark *features, + GAsyncReadyCallback callback, + gpointer user_data) +{ + ContactsAsyncData *data; + GSimpleAsyncResult *result; + ContactFeatureFlags feature_flags = 0; + guint minimal_feature_flags = G_MAXUINT; + const gchar **supported_interfaces; + GPtrArray *contacts_array; + GArray *handles; + guint i; + + g_return_if_fail (tp_proxy_is_prepared (self, + TP_CONNECTION_FEATURE_CONNECTED)); + g_return_if_fail (n_contacts >= 1); + g_return_if_fail (contacts != NULL); + + for (i = 0; i < n_contacts; i++) + { + g_return_if_fail (contacts[i]->priv->connection == self); + g_return_if_fail (contacts[i]->priv->identifier != NULL); + } + + if (features == NULL) + features = no_quarks; + + if (!get_feature_flags (features, &feature_flags)) + return; + + handles = g_array_sized_new (FALSE, FALSE, sizeof (TpHandle), n_contacts); + contacts_array = g_ptr_array_new_full (n_contacts, g_object_unref); + for (i = 0; i < n_contacts; i++) + { + /* feature flags that all contacts have */ + minimal_feature_flags &= contacts[i]->priv->has_features; + + /* Keep handles of contacts that does not already have all features */ + if ((feature_flags & (~contacts[i]->priv->has_features)) != 0) + g_array_append_val (handles, contacts[i]->priv->handle); + + /* Keep a ref on all contacts to ensure they do not disappear + * while upgrading them */ + g_ptr_array_add (contacts_array, g_object_ref (contacts[i])); + } + + /* Remove features that all contacts have */ + feature_flags &= (~minimal_feature_flags); + + supported_interfaces = contacts_bind_to_signals (self, feature_flags); + + result = g_simple_async_result_new ((GObject *) self, callback, user_data, + tp_connection_upgrade_contacts_async); + g_simple_async_result_set_op_res_gpointer (result, contacts_array, + (GDestroyNotify) g_ptr_array_unref); + + if (handles->len > 0 && supported_interfaces[0] != NULL) + { + data = contacts_async_data_new (result, feature_flags); + tp_cli_connection_interface_contacts_call_get_contact_attributes (self, + -1, handles, supported_interfaces, got_contact_attributes_cb, + data, (GDestroyNotify) contacts_async_data_free, NULL); + } + else + { + g_simple_async_result_complete_in_idle (result); + } + + g_free (supported_interfaces); + g_object_unref (result); + g_array_unref (handles); +} + +/** + * tp_connection_upgrade_contacts_finish: + * @self: a #TpConnection + * @result: a #GAsyncResult + * @contacts: (element-type TelepathyGLib.Contact) (transfer container) (out) (allow-none): + * a location to set a #GPtrArray of upgraded #TpContact, or %NULL. + * @error: a #GError to fill + * + * Finishes tp_connection_upgrade_contacts_async(). + * + * Returns: %TRUE on success, %FALSE otherwise. + * Since: 0.19.0 + */ +gboolean +tp_connection_upgrade_contacts_finish (TpConnection *self, + GAsyncResult *result, + GPtrArray **contacts, + GError **error) +{ + _tp_implement_finish_copy_pointer (self, + tp_connection_upgrade_contacts_async, g_ptr_array_ref, contacts); +} + void _tp_contact_set_is_blocked (TpContact *self, gboolean is_blocked) diff --git a/telepathy-glib/contact.h b/telepathy-glib/contact.h index 2910f1dc3..c089998b3 100644 --- a/telepathy-glib/contact.h +++ b/telepathy-glib/contact.h @@ -161,11 +161,13 @@ void tp_contact_set_contact_groups_async (TpContact *self, gboolean tp_contact_set_contact_groups_finish (TpContact *self, GAsyncResult *result, GError **error); +#ifndef TP_DISABLE_DEPRECATED typedef void (*TpConnectionContactsByHandleCb) (TpConnection *connection, guint n_contacts, TpContact * const *contacts, guint n_failed, const TpHandle *failed, const GError *error, gpointer user_data, GObject *weak_object); +_TP_DEPRECATED_IN_0_20 void tp_connection_get_contacts_by_handle (TpConnection *self, guint n_handles, const TpHandle *handles, const GQuark *features, @@ -176,6 +178,7 @@ typedef void (*TpConnectionUpgradeContactsCb) (TpConnection *connection, guint n_contacts, TpContact * const *contacts, const GError *error, gpointer user_data, GObject *weak_object); +_TP_DEPRECATED_IN_0_20_FOR(tp_connection_upgrade_contacts_async) void tp_connection_upgrade_contacts (TpConnection *self, guint n_contacts, TpContact * const *contacts, const GQuark *features, @@ -187,15 +190,41 @@ typedef void (*TpConnectionContactsByIdCb) (TpConnection *connection, const gchar * const *requested_ids, GHashTable *failed_id_errors, const GError *error, gpointer user_data, GObject *weak_object); +_TP_DEPRECATED_IN_0_20_FOR(tp_connection_get_contact_by_id_async) void tp_connection_get_contacts_by_id (TpConnection *self, guint n_ids, const gchar * const *ids, const GQuark *features, TpConnectionContactsByIdCb callback, gpointer user_data, GDestroyNotify destroy, GObject *weak_object); +#endif TpContact *tp_connection_dup_contact_if_possible (TpConnection *connection, TpHandle handle, const gchar *identifier); +_TP_AVAILABLE_IN_0_20 +void tp_connection_dup_contact_by_id_async (TpConnection *self, + const gchar *id, + const GQuark *features, + GAsyncReadyCallback callback, + gpointer user_data); +_TP_AVAILABLE_IN_0_20 +TpContact *tp_connection_dup_contact_by_id_finish (TpConnection *self, + GAsyncResult *result, + GError **error); + +_TP_AVAILABLE_IN_0_20 +void tp_connection_upgrade_contacts_async (TpConnection *self, + guint n_contacts, + TpContact * const *contacts, + const GQuark *features, + GAsyncReadyCallback callback, + gpointer user_data); +_TP_AVAILABLE_IN_0_20 +gboolean tp_connection_upgrade_contacts_finish (TpConnection *self, + GAsyncResult *result, + GPtrArray **contacts, + GError **error); + /* TP_CONTACT_FEATURE_CONTACT_BLOCKING */ _TP_AVAILABLE_IN_0_18 diff --git a/telepathy-glib/contacts-mixin.c b/telepathy-glib/contacts-mixin.c index 12ced5e56..3dacef0ea 100644 --- a/telepathy-glib/contacts-mixin.c +++ b/telepathy-glib/contacts-mixin.c @@ -84,6 +84,11 @@ static TpDBusPropertiesMixinPropImpl known_contacts_props[] = { { NULL } }; +static const gchar *always_included_interfaces[] = { + TP_IFACE_CONNECTION, + NULL +}; + static void tp_presence_mixin_get_contacts_dbus_property (GObject *object, GQuark interface, @@ -365,15 +370,11 @@ tp_contacts_mixin_get_contact_attributes_impl ( { TpBaseConnection *conn = TP_BASE_CONNECTION (iface); GHashTable *result; - const gchar *assumed_interfaces[] = { - TP_IFACE_CONNECTION, - NULL - }; TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (conn, context); result = tp_contacts_mixin_get_contact_attributes (G_OBJECT (conn), - handles, interfaces, assumed_interfaces); + handles, interfaces, always_included_interfaces); tp_svc_connection_interface_contacts_return_from_get_contact_attributes ( context, result); @@ -381,6 +382,48 @@ tp_contacts_mixin_get_contact_attributes_impl ( g_hash_table_unref (result); } +static void +tp_contacts_mixin_get_contact_by_id_impl ( + TpSvcConnectionInterfaceContacts *iface, + const gchar *id, + const gchar **interfaces, + DBusGMethodInvocation *context) +{ + TpBaseConnection *conn = TP_BASE_CONNECTION (iface); + TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (conn, + TP_HANDLE_TYPE_CONTACT); + TpHandle handle; + GArray *handles; + GHashTable *attributes; + GHashTable *result; + GError *error = NULL; + + TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (conn, context); + + handle = tp_handle_ensure (contact_repo, id, NULL, &error); + if (handle == 0) + { + dbus_g_method_return_error (context, error); + g_clear_error (&error); + return; + } + + handles = g_array_new (FALSE, FALSE, sizeof (TpHandle)); + g_array_append_val (handles, handle); + + attributes = tp_contacts_mixin_get_contact_attributes (G_OBJECT (conn), + handles, interfaces, always_included_interfaces); + + result = g_hash_table_lookup (attributes, GUINT_TO_POINTER (handle)); + g_assert (result != NULL); + + tp_svc_connection_interface_contacts_return_from_get_contact_by_id (context, + handle, result); + + g_array_unref (handles); + g_hash_table_unref (attributes); +} + /** * tp_contacts_mixin_iface_init: (skip) * @g_iface: A pointer to the #TpSvcConnectionInterfaceContacts in an object @@ -403,6 +446,7 @@ tp_contacts_mixin_iface_init (gpointer g_iface, gpointer iface_data) #define IMPLEMENT(x) tp_svc_connection_interface_contacts_implement_##x ( \ klass, tp_contacts_mixin_##x##_impl) IMPLEMENT(get_contact_attributes); + IMPLEMENT(get_contact_by_id); #undef IMPLEMENT } diff --git a/telepathy-glib/dbus-internal.h b/telepathy-glib/dbus-internal.h index bbdf7bddf..0d68a44be 100644 --- a/telepathy-glib/dbus-internal.h +++ b/telepathy-glib/dbus-internal.h @@ -40,6 +40,8 @@ GVariant * _tp_boxed_to_variant (GType gtype, const gchar *variant_type, gpointer boxed); +GHashTable * _tp_asv_from_vardict (GVariant *variant); + G_END_DECLS #endif /* __TP_INTERNAL_DBUS_GLIB_H__ */ diff --git a/telepathy-glib/dbus.c b/telepathy-glib/dbus.c index 5ba635200..553ea3e06 100644 --- a/telepathy-glib/dbus.c +++ b/telepathy-glib/dbus.c @@ -1886,3 +1886,29 @@ _tp_boxed_to_variant (GType gtype, return g_variant_ref_sink (ret); } + +/* + * _tp_asv_from_vardict: + * @variant: a #GVariant of type %G_VARIANT_TYPE_VARDICT + * + * Returns: (transfer full): a newly created #GHashTable of + * type #TP_HASH_TYPE_STRING_VARIANT_MAP + */ +GHashTable * +_tp_asv_from_vardict (GVariant *variant) +{ + GValue v = G_VALUE_INIT; + GHashTable *result; + + g_return_val_if_fail (variant != NULL, NULL); + g_return_val_if_fail (g_variant_is_of_type (variant, G_VARIANT_TYPE_VARDICT), + NULL); + + dbus_g_value_parse_g_variant (variant, &v); + g_assert (G_VALUE_HOLDS (&v, TP_HASH_TYPE_STRING_VARIANT_MAP)); + + result = g_value_dup_boxed (&v); + + g_value_unset (&v); + return result; +} diff --git a/telepathy-glib/debug-client.c b/telepathy-glib/debug-client.c index bd4f8bf9d..b76fc978e 100644 --- a/telepathy-glib/debug-client.c +++ b/telepathy-glib/debug-client.c @@ -44,7 +44,7 @@ * This module provides access to the auxiliary objects used to * implement #TpSvcDebug. * - * Since: 0.UNRELEASED + * Since: 0.19.0 */ /** @@ -52,7 +52,7 @@ * * The class of a #TpDebugClient. * - * Since: 0.UNRELEASED + * Since: 0.19.0 */ struct _TpDebugClientClass { TpProxyClass parent_class; @@ -65,7 +65,7 @@ struct _TpDebugClientClass { * * A proxy object for the debug interface of a Telepathy component. * - * Since: 0.UNRELEASED + * Since: 0.19.0 */ struct _TpDebugClient { TpProxy parent; @@ -203,7 +203,7 @@ tp_debug_client_class_init (TpDebugClientClass *klass) * This property is meaningless until the * %TP_DEBUG_CLIENT_FEATURE_CORE feature has been prepared. * - * Since: UNRELEASED + * Since: 0.19.0 */ spec = g_param_spec_boolean ("enabled", "enabled", "Enabled", @@ -219,7 +219,7 @@ tp_debug_client_class_init (TpDebugClientClass *klass) * Emitted when a #TpDebugMessage is generated if the TpDebugMessage:enabled * property is set to %TRUE. * - * Since: UNRELEASED + * Since: 0.19.0 */ signals[SIG_NEW_DEBUG_MESSAGE] = g_signal_new ("new-debug-message", G_OBJECT_CLASS_TYPE (klass), @@ -328,7 +328,7 @@ tp_debug_client_list_features (TpProxyClass *klass) * tp_proxy_or_subclass_hook_on_interface_add() with first argument * %TP_TYPE_DEBUG_CLIENT. * - * Since: 0.UNRELEASED + * Since: 0.19.0 */ void tp_debug_client_init_known_interfaces (void) @@ -360,7 +360,7 @@ tp_debug_client_init_known_interfaces (void) * * Returns: a new debug client proxy, or %NULL on invalid arguments * - * Since: 0.UNRELEASED + * Since: 0.19.0 */ TpDebugClient * tp_debug_client_new ( @@ -405,7 +405,7 @@ set_enabled_cb ( * Enable or disable publishing of debug messages on the bus by the component * owning @self's bus name. * - * Since: 0.UNRELEASED + * Since: 0.19.0 */ void tp_debug_client_set_enabled_async ( @@ -434,7 +434,7 @@ tp_debug_client_set_enabled_async ( * Finishes tp_debug_client_set_enabled_async(). * * Returns: %TRUE, if the operation suceeded, %FALSE otherwise - * Since: UNRELEASED + * Since: 0.19.0 */ gboolean tp_debug_client_set_enabled_finish ( @@ -453,7 +453,7 @@ tp_debug_client_set_enabled_finish ( * * Returns: the value of #TpDebugClient:enabled property * - * Since: UNRELEASED + * Since: 0.19.0 */ gboolean tp_debug_client_is_enabled (TpDebugClient *self) @@ -512,7 +512,7 @@ out: * use tp_debug_client_get_messages_finish() to retrieve the #TpDebugMessage * objects. * - * Since: UNRELEASED + * Since: 0.19.0 */ void tp_debug_client_get_messages_async ( @@ -538,7 +538,7 @@ tp_debug_client_get_messages_async ( * Returns: (transfer full) (type GLib.PtrArray) (element-type TelepathyGLib.DebugMessage): * a #GPtrArray of #TpDebugMessage * - * Since: UNRELEASED + * Since: 0.19.0 */ GPtrArray * tp_debug_client_get_messages_finish (TpDebugClient *self, diff --git a/telepathy-glib/debug-internal.h b/telepathy-glib/debug-internal.h index cea13dacf..4f8c0fea7 100644 --- a/telepathy-glib/debug-internal.h +++ b/telepathy-glib/debug-internal.h @@ -35,7 +35,8 @@ typedef enum TP_DEBUG_ROOM_CONFIG = 1 << 17, TP_DEBUG_CALL = 1 << 18, /* Quis custodiet ipsos custodes? */ - TP_DEBUG_DEBUGGER = 1 << 19 + TP_DEBUG_DEBUGGER = 1 << 19, + TP_DEBUG_TLS = 1 << 20 } TpDebugFlags; gboolean _tp_debug_flag_is_set (TpDebugFlags flag); diff --git a/telepathy-glib/debug-message.c b/telepathy-glib/debug-message.c index ff947a7e4..284eef793 100644 --- a/telepathy-glib/debug-message.c +++ b/telepathy-glib/debug-message.c @@ -42,7 +42,7 @@ * * Data structure representing a #TpDebugMessage. * - * Since: UNRELEASED + * Since: 0.19.0 */ /** @@ -50,7 +50,7 @@ * * The class of a #TpDebugMessage. * - * Since: UNRELEASED + * Since: 0.19.0 */ G_DEFINE_TYPE (TpDebugMessage, tp_debug_message, G_TYPE_OBJECT) @@ -133,7 +133,7 @@ tp_debug_message_class_init ( * * Timestamp of the debug message. * - * Since: UNRELEASED + * Since: 0.19.0 */ spec = g_param_spec_boxed ("time", "time", "Time", @@ -146,7 +146,7 @@ tp_debug_message_class_init ( * * Domain of the debug message. * - * Since: UNRELEASED + * Since: 0.19.0 */ spec = g_param_spec_string ("domain", "domain", "Domain", @@ -159,7 +159,7 @@ tp_debug_message_class_init ( * * Category of the debug message, or %NULL if none was specified. * - * Since: UNRELEASED + * Since: 0.19.0 */ spec = g_param_spec_string ("category", "category", "Category", @@ -172,7 +172,7 @@ tp_debug_message_class_init ( * * A #GLogLevelFlags representing the level of the debug message. * - * Since: UNRELEASED + * Since: 0.19.0 */ spec = g_param_spec_uint ("level", "level", "Level", @@ -185,7 +185,7 @@ tp_debug_message_class_init ( * * Text of the debug message, stripped from its trailing whitespaces. * - * Since: UNRELEASED + * Since: 0.19.0 */ spec = g_param_spec_string ("message", "message", "Message", @@ -271,7 +271,7 @@ _tp_debug_message_new (gdouble timestamp, * * Returns: (transfer none): the value of #TpDebugMessage:time property * - * Since: UNRELEASED + * Since: 0.19.0 */ GDateTime * tp_debug_message_get_time (TpDebugMessage *self) @@ -287,7 +287,7 @@ tp_debug_message_get_time (TpDebugMessage *self) * * Returns: the value of #TpDebugMessage:domain property * - * Since: UNRELEASED + * Since: 0.19.0 */ const gchar * tp_debug_message_get_domain (TpDebugMessage *self) @@ -303,7 +303,7 @@ tp_debug_message_get_domain (TpDebugMessage *self) * * Returns: the value of #TpDebugMessage:category property * - * Since: UNRELEASED + * Since: 0.19.0 */ const char * tp_debug_message_get_category (TpDebugMessage *self) @@ -319,7 +319,7 @@ tp_debug_message_get_category (TpDebugMessage *self) * * Returns: the value of #TpDebugMessage:level property * - * Since: UNRELEASED + * Since: 0.19.0 */ GLogLevelFlags tp_debug_message_get_level (TpDebugMessage *self) @@ -335,7 +335,7 @@ tp_debug_message_get_level (TpDebugMessage *self) * * Returns: the value of #TpDebugMessage:message property * - * Since: UNRELEASED + * Since: 0.19.0 */ const gchar * tp_debug_message_get_message (TpDebugMessage *self) diff --git a/telepathy-glib/debug.c b/telepathy-glib/debug.c index 048cce8c1..36a673223 100644 --- a/telepathy-glib/debug.c +++ b/telepathy-glib/debug.c @@ -54,6 +54,8 @@ * <listitem><literal>contact-lists</literal> - the #TpBaseContactList * (service)</listitem> * <listitem><literal>debugger</literal> - #TpDebugClient objects</listitem> + * <listitem><literal>tls</literal> - #TpTLSCertificate objects + * (client)</listitem> * <listitem><literal>all</literal> - all of the above</listitem> * </itemizedlist> */ @@ -101,6 +103,7 @@ static GDebugKey keys[] = { { "room-config", TP_DEBUG_ROOM_CONFIG }, { "call", TP_DEBUG_CALL }, { "debugger", TP_DEBUG_DEBUGGER }, + { "tls", TP_DEBUG_TLS }, { 0, } }; @@ -135,6 +138,7 @@ static DebugKeyToDomain key_to_domain[] = { { TP_DEBUG_SASL, G_LOG_DOMAIN "/sasl" }, { TP_DEBUG_ROOM_CONFIG, G_LOG_DOMAIN "/room-config" }, { TP_DEBUG_DEBUGGER, G_LOG_DOMAIN "/debugger" }, + { TP_DEBUG_TLS, G_LOG_DOMAIN "/tls" }, { 0, NULL } }; diff --git a/telepathy-glib/defs.h b/telepathy-glib/defs.h index df0405905..6b497b540 100644 --- a/telepathy-glib/defs.h +++ b/telepathy-glib/defs.h @@ -37,33 +37,25 @@ G_BEGIN_DECLS #if (TP_MINOR_VERSION == 99) /* special case for telepathy-glib 1.0 prereleases */ # define _TP_VERSION_CUR_STABLE (_TP_ENCODE_VERSION (TP_MAJOR_VERSION + 1, 0)) -# define _TP_VERSION_PREV_STABLE (_TP_ENCODE_VERSION (TP_MAJOR_VERSION + 1, 0)) #elif (TP_MINOR_VERSION == 0) /* special case for telepathy-glib 1.0 itself */ # define _TP_VERSION_CUR_STABLE (_TP_ENCODE_VERSION (TP_MAJOR_VERSION, 0)) -# define _TP_VERSION_PREV_STABLE (_TP_ENCODE_VERSION (TP_MAJOR_VERSION, 0)) #elif (TP_MICRO_VERSION >= 99 && (TP_MINOR_VERSION % 2) == 0) /* development branch about to start (0.18.999.1) */ # define _TP_VERSION_CUR_STABLE \ (_TP_ENCODE_VERSION (TP_MAJOR_VERSION, TP_MINOR_VERSION + 2)) -# define _TP_VERSION_PREV_STABLE \ - (_TP_ENCODE_VERSION (TP_MAJOR_VERSION, TP_MINOR_VERSION)) #elif (TP_MINOR_VERSION % 2) /* development branch */ # define _TP_VERSION_CUR_STABLE \ (_TP_ENCODE_VERSION (TP_MAJOR_VERSION, TP_MINOR_VERSION + 1)) -# define _TP_VERSION_PREV_STABLE \ - (_TP_ENCODE_VERSION (TP_MAJOR_VERSION, TP_MINOR_VERSION - 1)) #else /* stable branch */ # define _TP_VERSION_CUR_STABLE \ (_TP_ENCODE_VERSION (TP_MAJOR_VERSION, TP_MINOR_VERSION)) -# define _TP_VERSION_PREV_STABLE \ - (_TP_ENCODE_VERSION (TP_MAJOR_VERSION, TP_MINOR_VERSION - 2)) #endif #ifndef TP_VERSION_MIN_REQUIRED -# define TP_VERSION_MIN_REQUIRED (_TP_VERSION_PREV_STABLE) +# define TP_VERSION_MIN_REQUIRED (_TP_VERSION_CUR_STABLE) #endif #ifndef TP_VERSION_MAX_ALLOWED diff --git a/telepathy-glib/extra-gtkdoc.h b/telepathy-glib/extra-gtkdoc.h index 0dbf4244a..cad2cd874 100644 --- a/telepathy-glib/extra-gtkdoc.h +++ b/telepathy-glib/extra-gtkdoc.h @@ -350,7 +350,7 @@ * @title: Version information * @short_description: Checking the telepathy-glib version * - * Since 0.UNRELEASED, telepathy-glib provides version information similar + * Since 0.19.0, telepathy-glib provides version information similar * to that used in GLib. * * Typical usage from configure.ac is similar to GLib's: @@ -372,7 +372,7 @@ * ${TP_CFLAGS} ${TP_LIBS} * ]| * - * This functionality was added in telepathy-glib 0.UNRELEASED, but it + * This functionality was added in telepathy-glib 0.19.0, but it * is safe to define the TP_VERSION_MIN_REQUIRED and TP_VERSION_MAX_ALLOWED * macros even for older versions of telepathy-glib, as long as you do * not try to expand them. diff --git a/telepathy-glib/introspection.am b/telepathy-glib/introspection.am index 9b0f3e89c..b6bdcc569 100644 --- a/telepathy-glib/introspection.am +++ b/telepathy-glib/introspection.am @@ -72,6 +72,8 @@ TelepathyGLib_1_gir_FILES = \ $(srcdir)/channel-dispatcher.c $(srcdir)/channel-dispatcher.h \ $(srcdir)/debug-client.c $(srcdir)/debug-client.h \ $(srcdir)/debug-message.c $(srcdir)/debug-message.h \ + $(srcdir)/tls-certificate.c $(srcdir)/tls-certificate.h \ + $(srcdir)/tls-certificate-rejection.c $(srcdir)/tls-certificate-rejection.h \ $(srcdir)/errors.c $(srcdir)/errors.h \ $(srcdir)/room-list.c $(srcdir)/room-list.h \ $(srcdir)/room-info.c $(srcdir)/room-info.h \ diff --git a/telepathy-glib/message-mixin.c b/telepathy-glib/message-mixin.c index eb416be02..c46efdda3 100644 --- a/telepathy-glib/message-mixin.c +++ b/telepathy-glib/message-mixin.c @@ -46,6 +46,15 @@ * tp_message_mixin_implement_sending() in the constructor function. If you do * not, any attempt to send a message will fail with NotImplemented. * + * To support chat state, you must call + * tp_message_mixin_implement_send_chat_state() in the constructor function, and + * include the following in the fourth argument of G_DEFINE_TYPE_WITH_CODE(): + * + * <informalexample><programlisting> + * G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_CHAT_STATE, + * tp_message_mixin_chat_state_iface_init); + * </programlisting></informalexample> + * * Since: 0.7.21 */ @@ -97,6 +106,18 @@ struct _TpMessageMixinPrivate /* Receiving */ guint recv_id; GQueue *pending; + + /* ChatState */ + + /* TpHandle -> TpChannelChatState */ + GHashTable *chat_states; + TpMessageMixinSendChatStateImpl send_chat_state; + /* FALSE unless at least one chat state notification has been sent; <gone/> + * will only be sent when the channel closes if this is TRUE. This prevents + * opening a channel and closing it immediately sending a spurious <gone/> to + * the peer. + */ + gboolean send_gone; }; @@ -251,6 +272,185 @@ tp_message_mixin_implement_sending (GObject *object, (gchar **) supported_content_types); } +static TpChannelChatState +lookup_current_chat_state (TpMessageMixin *mixin, + TpHandle member) +{ + gpointer tmp; + + if (g_hash_table_lookup_extended (mixin->priv->chat_states, + GUINT_TO_POINTER (member), NULL, &tmp)) + { + return GPOINTER_TO_UINT (tmp); + } + + return TP_CHANNEL_CHAT_STATE_INACTIVE; +} + +/** + * tp_message_mixin_change_chat_state: + * @object: an instance of the implementation that uses this mixin + * @member: a member of this chat + * @state: the new state to set + * + * Change the current chat state of @member to be @state. This emits + * ChatStateChanged signal and update ChatStates property. + * + * Since: 0.19.0 + */ +void +tp_message_mixin_change_chat_state (GObject *object, + TpHandle member, + TpChannelChatState state) +{ + TpMessageMixin *mixin = TP_MESSAGE_MIXIN (object); + + g_return_if_fail (state < TP_NUM_CHANNEL_CHAT_STATES); + + if (state == lookup_current_chat_state (mixin, member)) + return; + + if (state == TP_CHANNEL_CHAT_STATE_INACTIVE || + state == TP_CHANNEL_CHAT_STATE_GONE) + { + g_hash_table_remove (mixin->priv->chat_states, + GUINT_TO_POINTER (member)); + } + else + { + g_hash_table_insert (mixin->priv->chat_states, + GUINT_TO_POINTER (member), + GUINT_TO_POINTER (state)); + } + + tp_svc_channel_interface_chat_state_emit_chat_state_changed (object, + member, state); +} + +/** + * TpMessageMixinSendChatStateImpl: + * @object: an instance of the implementation that uses this mixin + * @state: a #TpChannelChatState to be send + * @error: a #GError to fill + * + * Signature of a virtual method which may be implemented to allow sending chat + * state. + * + * Returns: %TRUE on success, %FALSE otherwise. + * Since: 0.19.0 + */ + +/** + * tp_message_mixin_implement_send_chat_state: + * @object: an instance of the implementation that uses this mixin + * @send_chat_state: send our chat state + * + * Set the callback used to implement SetChatState. This must be called from the + * init, constructor or constructed callback, after tp_message_mixin_init(), + * and may only be called once per object. + * + * Since: 0.19.0 + */ +void +tp_message_mixin_implement_send_chat_state (GObject *object, + TpMessageMixinSendChatStateImpl send_chat_state) +{ + TpMessageMixin *mixin = TP_MESSAGE_MIXIN (object); + + g_return_if_fail (mixin->priv->send_chat_state == NULL); + + mixin->priv->send_chat_state = send_chat_state; +} + +/** + * tp_message_mixin_maybe_send_gone: + * @object: An instance of the implementation that uses this mixin + * + * Send #TP_CHANNEL_CHAT_STATE_GONE if needed. This should be called on private + * chats when channel is closed. + * + * Since: 0.19.0 + */ +void +tp_message_mixin_maybe_send_gone (GObject *object) +{ + TpMessageMixin *mixin = TP_MESSAGE_MIXIN (object); + + if (mixin->priv->send_gone && !TP_HAS_GROUP_MIXIN (object) && + mixin->priv->send_chat_state != NULL) + { + mixin->priv->send_chat_state (object, TP_CHANNEL_CHAT_STATE_GONE, NULL); + } + + mixin->priv->send_gone = FALSE; +} + +/* FIXME: Use tp_base_channel_get_self_handle() when TpMessageMixin requires + * TpBaseChannel. See bug #49366 */ +static TpHandle +get_self_handle (GObject *object) +{ + TpMessageMixin *mixin = TP_MESSAGE_MIXIN (object); + + if (TP_HAS_GROUP_MIXIN (object)) + { + guint ret = 0; + + tp_group_mixin_get_self_handle (object, &ret, NULL); + if (ret != 0) + return ret; + } + + return tp_base_connection_get_self_handle (mixin->priv->connection); +} + +static void +tp_message_mixin_set_chat_state_async (TpSvcChannelInterfaceChatState *iface, + guint state, + DBusGMethodInvocation *context) +{ + GObject *object = (GObject *) iface; + TpMessageMixin *mixin = TP_MESSAGE_MIXIN (object); + GError *error = NULL; + + if (mixin->priv->send_chat_state == NULL) + { + tp_dbus_g_method_return_not_implemented (context); + return; + } + + if (state >= TP_NUM_CHANNEL_CHAT_STATES) + { + DEBUG ("invalid chat state %u", state); + + g_set_error (&error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, + "invalid state: %u", state); + goto error; + } + + if (state == TP_CHANNEL_CHAT_STATE_GONE) + { + /* We cannot explicitly set the Gone state */ + DEBUG ("you may not explicitly set the Gone state"); + + g_set_error (&error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, + "you may not explicitly set the Gone state"); + goto error; + } + + if (!mixin->priv->send_chat_state (object, state, &error)) + goto error; + + mixin->priv->send_gone = TRUE; + tp_message_mixin_change_chat_state (object, get_self_handle (object), state); + + tp_svc_channel_interface_chat_state_return_from_set_chat_state (context); + return; + +error: + dbus_g_method_return_error (context, error); + g_clear_error (&error); +} /** * tp_message_mixin_init: @@ -293,6 +493,8 @@ tp_message_mixin_init (GObject *obj, mixin->priv->connection = g_object_ref (connection); mixin->priv->supported_content_types = g_new0 (gchar *, 1); + + mixin->priv->chat_states = g_hash_table_new (NULL, NULL); } @@ -339,6 +541,8 @@ tp_message_mixin_finalize (GObject *obj) g_object_unref (mixin->priv->connection); + g_hash_table_unref (mixin->priv->chat_states); + g_slice_free (TpMessageMixinPrivate, mixin->priv); } @@ -560,7 +764,6 @@ struct _TpMessageMixinOutgoingMessagePrivate { gboolean messages:1; }; - /** * tp_message_mixin_sent: * @object: An object implementing the Text interface with this mixin @@ -611,21 +814,13 @@ tp_message_mixin_sent (GObject *object, else { GHashTable *header = g_ptr_array_index (message->parts, 0); - TpHandle self_handle = 0; + + mixin->priv->send_gone = TRUE; if (tp_asv_get_uint64 (header, "message-sent", NULL) == 0) tp_message_set_uint64 (message, 0, "message-sent", time (NULL)); - if (TP_HAS_GROUP_MIXIN (object)) - { - tp_group_mixin_get_self_handle (object, &self_handle, NULL); - } - - if (self_handle == 0) - self_handle = tp_base_connection_get_self_handle ( - mixin->priv->connection); - - tp_cm_message_set_sender (message, self_handle); + tp_cm_message_set_sender (message, get_self_handle (object)); /* emit Sent and MessageSent */ @@ -770,10 +965,18 @@ tp_message_mixin_init_dbus_properties (GObjectClass *cls) { "DeliveryReportingSupport", NULL, NULL }, { NULL } }; + static TpDBusPropertiesMixinPropImpl chat_state_props[] = { + { "ChatStates", NULL, NULL }, + { NULL } + }; tp_dbus_properties_mixin_implement_interface (cls, TP_IFACE_QUARK_CHANNEL_TYPE_TEXT, tp_message_mixin_get_dbus_property, NULL, props); + + tp_dbus_properties_mixin_implement_interface (cls, + TP_IFACE_QUARK_CHANNEL_INTERFACE_CHAT_STATE, + tp_message_mixin_get_dbus_property, NULL, chat_state_props); } @@ -804,6 +1007,7 @@ tp_message_mixin_get_dbus_property (GObject *object, static GQuark q_message_part_support_flags = 0; static GQuark q_delivery_reporting_support_flags = 0; static GQuark q_message_types = 0; + static GQuark q_chat_states = 0; if (G_UNLIKELY (q_pending_messages == 0)) { @@ -816,11 +1020,14 @@ tp_message_mixin_get_dbus_property (GObject *object, g_quark_from_static_string ("DeliveryReportingSupport"); q_message_types = g_quark_from_static_string ("MessageTypes"); + q_chat_states = + g_quark_from_static_string ("ChatStates"); } mixin = TP_MESSAGE_MIXIN (object); - g_return_if_fail (interface == TP_IFACE_QUARK_CHANNEL_TYPE_TEXT); + g_return_if_fail (interface == TP_IFACE_QUARK_CHANNEL_TYPE_TEXT || + interface == TP_IFACE_QUARK_CHANNEL_INTERFACE_CHAT_STATE); g_return_if_fail (object != NULL); g_return_if_fail (name != 0); g_return_if_fail (value != NULL); @@ -861,6 +1068,10 @@ tp_message_mixin_get_dbus_property (GObject *object, { g_value_set_boxed (value, mixin->priv->msg_types); } + else if (name == q_chat_states) + { + g_value_set_boxed (value, mixin->priv->chat_states); + } } @@ -886,3 +1097,26 @@ tp_message_mixin_iface_init (gpointer g_iface, IMPLEMENT (send_message); #undef IMPLEMENT } + +/** + * tp_message_mixin_chat_state_iface_init: + * @g_iface: A pointer to the #TpSvcChannelInterfaceChatStateClass in an object + * class + * @iface_data: Ignored + * + * Fill in this mixin's ChatState method implementations in the given interface + * vtable. + * + * Since: 0.19.0 + */ +void +tp_message_mixin_chat_state_iface_init (gpointer g_iface, + gpointer iface_data) +{ + TpSvcChannelInterfaceChatStateClass *klass = g_iface; + +#define IMPLEMENT(x) tp_svc_channel_interface_chat_state_implement_##x (\ + klass, tp_message_mixin_##x##_async) + IMPLEMENT (set_chat_state); +#undef IMPLEMENT +} diff --git a/telepathy-glib/message-mixin.h b/telepathy-glib/message-mixin.h index 7c0ccbae0..4696f4feb 100644 --- a/telepathy-glib/message-mixin.h +++ b/telepathy-glib/message-mixin.h @@ -67,9 +67,28 @@ void tp_message_mixin_implement_sending (GObject *object, TpDeliveryReportingSupportFlags delivery_reporting_support_flags, const gchar * const * supported_content_types); +/* ChatState */ + +typedef gboolean (*TpMessageMixinSendChatStateImpl) (GObject *object, + TpChannelChatState state, + GError **error); + +_TP_AVAILABLE_IN_0_20 +void tp_message_mixin_change_chat_state (GObject *object, + TpHandle member, + TpChannelChatState state); + +_TP_AVAILABLE_IN_0_20 +void tp_message_mixin_implement_send_chat_state (GObject *object, + TpMessageMixinSendChatStateImpl send_chat_state); + +_TP_AVAILABLE_IN_0_20 +void tp_message_mixin_maybe_send_gone (GObject *object); /* Initialization */ void tp_message_mixin_iface_init (gpointer g_iface, gpointer iface_data); +void tp_message_mixin_chat_state_iface_init (gpointer g_iface, + gpointer iface_data); void tp_message_mixin_init (GObject *obj, gsize offset, TpBaseConnection *connection); diff --git a/telepathy-glib/proxy.c b/telepathy-glib/proxy.c index 4c3715871..739790638 100644 --- a/telepathy-glib/proxy.c +++ b/telepathy-glib/proxy.c @@ -108,7 +108,7 @@ tp_dbus_errors_quark (void) * * 1 more than the highest valid #TpDBusError at the time of compilation * - * Since: 0.UNRELEASED + * Since: 0.19.0 */ /** @@ -1990,6 +1990,9 @@ static void prepare_feature (TpProxy *self, const TpProxyFeature *feature) { + /* If no function is set, then subclass is supposed to call + * _tp_proxy_set_feature_prepared() itself. This is used by features prepared + * from constructed. */ if (feature->prepare_async == NULL) return; diff --git a/telepathy-glib/room-info.c b/telepathy-glib/room-info.c index 2412ee5f1..1843b1d0f 100644 --- a/telepathy-glib/room-info.c +++ b/telepathy-glib/room-info.c @@ -42,7 +42,7 @@ * * Data structure representing a #TpRoomInfo. * - * Since: UNRELEASED + * Since: 0.19.0 */ /** @@ -50,7 +50,7 @@ * * The class of a #TpRoomInfo. * - * Since: UNRELEASED + * Since: 0.19.0 */ G_DEFINE_TYPE (TpRoomInfo, tp_room_info, G_TYPE_OBJECT) @@ -127,7 +127,7 @@ _tp_room_info_new (GValueArray *dbus_struct) * * Returns: the #TpHandle of the room * - * Since: UNRELEASED + * Since: 0.19.0 */ TpHandle tp_room_info_get_handle (TpRoomInfo *self) @@ -144,7 +144,7 @@ tp_room_info_get_handle (TpRoomInfo *self) * Returns: a string representing the D-Bus interface name of * the channel type of the room * - * Since: UNRELEASED + * Since: 0.19.0 */ const gchar * tp_room_info_get_channel_type (TpRoomInfo *self) @@ -161,7 +161,7 @@ tp_room_info_get_channel_type (TpRoomInfo *self) * Returns: the identifier of the room (as would be returned * by inspecting the #TpHandle returned by tp_room_info_get_handle()) * - * Since: UNRELEASED + * Since: 0.19.0 */ const gchar * tp_room_info_get_handle_name (TpRoomInfo *self) @@ -178,7 +178,7 @@ tp_room_info_get_handle_name (TpRoomInfo *self) * Returns: the human-readable name of the room if different * from the handle * - * Since: UNRELEASED + * Since: 0.19.0 */ const gchar * tp_room_info_get_name (TpRoomInfo *self) @@ -194,7 +194,7 @@ tp_room_info_get_name (TpRoomInfo *self) * * Returns: a description of the room's overall purpose * - * Since: UNRELEASED + * Since: 0.19.0 */ const gchar * tp_room_info_get_description (TpRoomInfo *self) @@ -210,7 +210,7 @@ tp_room_info_get_description (TpRoomInfo *self) * * Returns: the current subject of conversation in the room * - * Since: UNRELEASED + * Since: 0.19.0 */ const gchar * tp_room_info_get_subject (TpRoomInfo *self) @@ -228,7 +228,7 @@ tp_room_info_get_subject (TpRoomInfo *self) * * Returns: the number of members in the room * - * Since: UNRELEASED + * Since: 0.19.0 */ guint tp_room_info_get_members_count (TpRoomInfo *self, @@ -247,7 +247,7 @@ tp_room_info_get_members_count (TpRoomInfo *self, * * Returns: %TRUE if the room requires a password to enter * - * Since: UNRELEASED + * Since: 0.19.0 */ gboolean tp_room_info_get_requires_password (TpRoomInfo *self, @@ -266,7 +266,7 @@ tp_room_info_get_requires_password (TpRoomInfo *self, * * Returns: %TRUE if you cannot join the room, but must be invited * - * Since: UNRELEASED + * Since: 0.19.0 */ gboolean tp_room_info_get_invite_only (TpRoomInfo *self, @@ -283,7 +283,7 @@ tp_room_info_get_invite_only (TpRoomInfo *self, * * Returns: the human-readable identifier of the room * - * Since: UNRELEASED + * Since: 0.19.0 */ const gchar * tp_room_info_get_room_id (TpRoomInfo *self) @@ -299,7 +299,7 @@ tp_room_info_get_room_id (TpRoomInfo *self) * * Returns: the DNS name of the server hosting the room * - * Since: UNRELEASED + * Since: 0.19.0 */ const gchar * tp_room_info_get_server (TpRoomInfo *self) diff --git a/telepathy-glib/room-list.c b/telepathy-glib/room-list.c index 3828672f3..98fd7e0e0 100644 --- a/telepathy-glib/room-list.c +++ b/telepathy-glib/room-list.c @@ -31,7 +31,7 @@ * * Data structure representing a #TpRoomList. * - * Since: UNRELEASED + * Since: 0.19.0 */ /** @@ -39,7 +39,7 @@ * * The class of a #TpRoomList. * - * Since: UNRELEASED + * Since: 0.19.0 */ #include <config.h> @@ -237,7 +237,7 @@ tp_room_list_class_init (TpRoomListClass *klass) * * The #TpAccount to use for the room listing. * - * Since: UNRELEASED + * Since: 0.19.0 */ param_spec = g_param_spec_object ("account", "account", "TpAccount", @@ -251,7 +251,7 @@ tp_room_list_class_init (TpRoomListClass *klass) * The DNS name of the server whose rooms are listed by this channel, or * %NULL. * - * Since: 0.UNRELEASED + * Since: 0.19.0 */ param_spec = g_param_spec_string ("server", "Server", "The server associated with the channel", @@ -267,7 +267,7 @@ tp_room_list_class_init (TpRoomListClass *klass) * This property is meaningless until the * %TP_ROOM_LIST_FEATURE_LISTING feature has been prepared. * - * Since: 0.UNRELEASED + * Since: 0.19.0 */ param_spec = g_param_spec_boolean ("listing", "Listing", "TRUE if the channel is listing rooms", @@ -285,7 +285,7 @@ tp_room_list_class_init (TpRoomListClass *klass) * User should take his own reference on @room if he plans to * continue using it once the signal callback has returned. * - * Since: UNRELEASED + * Since: 0.19.0 */ signals[SIG_GOT_ROOM] = g_signal_new ("got-room", G_OBJECT_CLASS_TYPE (klass), @@ -302,7 +302,7 @@ tp_room_list_class_init (TpRoomListClass *klass) * Fired when something goes wrong while listing the channels; see @error * for details. * - * Since: UNRELEASED + * Since: 0.19.0 */ signals[SIG_FAILED] = g_signal_new ("failed", G_OBJECT_CLASS_TYPE (klass), @@ -329,7 +329,7 @@ tp_room_list_init (TpRoomList *self) * * Returns: (transfer none): the value of #TpRoomList:account property * - * Since: UNRELEASED + * Since: 0.19.0 */ TpAccount * tp_room_list_get_account (TpRoomList *self) @@ -345,7 +345,7 @@ tp_room_list_get_account (TpRoomList *self) * * Returns: the value of #TpRoomList:server property * - * Since: UNRELEASED + * Since: 0.19.0 */ const gchar * tp_room_list_get_server (TpRoomList *self) @@ -361,7 +361,7 @@ tp_room_list_get_server (TpRoomList *self) * * Returns: the value of #TpRoomList:listing property * - * Since: UNRELEASED + * Since: 0.19.0 */ gboolean tp_room_list_is_listing (TpRoomList *self) @@ -391,7 +391,7 @@ list_rooms_cb (TpChannel *channel, * signal to get the rooms found. * Errors will be reported using the TpRoomList::failed signal. * - * Since: UNRELEASED + * Since: 0.19.0 */ void tp_room_list_start (TpRoomList *self) @@ -558,7 +558,7 @@ async_initable_iface_init (GAsyncInitableIface *iface) * * <!-- --> * - * Since: UNRELEASED + * Since: 0.19.0 */ void tp_room_list_new_async (TpAccount *account, @@ -585,7 +585,7 @@ tp_room_list_new_async (TpAccount *account, * Returns: (transfer full): a new #TpRoomList object, or %NULL * in case of error. * - * Since: UNRELEASED + * Since: 0.19.0 */ TpRoomList * tp_room_list_new_finish (GAsyncResult *result, diff --git a/telepathy-glib/stream-tube-channel.c b/telepathy-glib/stream-tube-channel.c index c41f380cd..d74e7f435 100644 --- a/telepathy-glib/stream-tube-channel.c +++ b/telepathy-glib/stream-tube-channel.c @@ -1035,12 +1035,15 @@ connection_identified (TpStreamTubeChannel *self, features = tp_client_factory_dup_contact_features ( tp_proxy_get_factory (connection), connection); + /* Spec does not give the id with the handle */ + G_GNUC_BEGIN_IGNORE_DEPRECATIONS /* Pass the ref on tube_conn to the function */ tp_connection_get_contacts_by_handle (connection, 1, &handle, (const GQuark *) features->data, _new_remote_connection_with_contact, tube_conn, g_object_unref, G_OBJECT (self)); + G_GNUC_END_IGNORE_DEPRECATIONS g_array_unref (features); } diff --git a/telepathy-glib/telepathy-glib.h b/telepathy-glib/telepathy-glib.h index 0e3ac96de..7d72c3d43 100644 --- a/telepathy-glib/telepathy-glib.h +++ b/telepathy-glib/telepathy-glib.h @@ -107,6 +107,7 @@ #include <telepathy-glib/stream-tube-channel.h> #include <telepathy-glib/stream-tube-connection.h> #include <telepathy-glib/text-channel.h> +#include <telepathy-glib/tls-certificate.h> #undef __TP_IN_GLIB_H__ #endif /* __TP_GLIB_H__ */ diff --git a/telepathy-glib/text-channel.c b/telepathy-glib/text-channel.c index 3325b061d..95688a929 100644 --- a/telepathy-glib/text-channel.c +++ b/telepathy-glib/text-channel.c @@ -105,6 +105,7 @@ enum /* signals */ SIG_MESSAGE_RECEIVED, SIG_PENDING_MESSAGE_REMOVED, SIG_MESSAGE_SENT, + SIG_CONTACT_CHAT_STATE_CHANGED, LAST_SIGNAL }; @@ -357,6 +358,42 @@ message_sent_cb (TpChannel *channel, } static void +chat_state_changed_cb (TpTextChannel *self, + TpHandle handle, + TpChannelChatState state) +{ + TpConnection *conn; + TpContact *contact; + + /* We have only an handle, but since we guarantee "contact-chat-state-changed" + * to be emitted only if TP_CHANNEL_FEATURE_GROUP and + * TP_CHANNEL_FEATURE_CONTACTS has been prepared, we should already have its + * TpContact. If the TpContact does not exist, telling its chat state is + * useless anyway. */ + conn = tp_channel_borrow_connection ((TpChannel *) self); + contact = tp_connection_dup_contact_if_possible (conn, handle, NULL); + if (contact == NULL) + return; + + g_signal_emit (self, signals[SIG_CONTACT_CHAT_STATE_CHANGED], 0, + contact, state); + + g_object_unref (contact); +} + +static void +tp_text_channel_prepare_chat_states_async (TpProxy *proxy, + const TpProxyFeature *feature, + GAsyncReadyCallback callback, + gpointer user_data) +{ + /* This feature depends on TP_CHANNEL_FEATURE_CHAT_STATES so it's already + * prepared. */ + tp_simple_async_report_success_in_idle ((GObject *) proxy, + callback, user_data, tp_text_channel_prepare_chat_states_async); +} + +static void tp_text_channel_constructed (GObject *obj) { TpTextChannel *self = (TpTextChannel *) obj; @@ -383,6 +420,11 @@ tp_text_channel_constructed (GObject *obj) return; } + /* Forward TpChannel::chat-state-changed as + * TpTextChannel::contact-chat-state-changed */ + g_signal_connect (self, "chat-state-changed", + G_CALLBACK (chat_state_changed_cb), NULL); + props = tp_channel_borrow_immutable_properties (TP_CHANNEL (self)); self->priv->supported_content_types = (GStrv) tp_asv_get_strv (props, @@ -786,6 +828,7 @@ tp_text_channel_prepare_sms_async (TpProxy *proxy, enum { FEAT_PENDING_MESSAGES, FEAT_SMS, + FEAT_CHAT_STATES, N_FEAT }; @@ -794,6 +837,7 @@ tp_text_channel_list_features (TpProxyClass *cls G_GNUC_UNUSED) { static TpProxyFeature features[N_FEAT + 1] = { { 0 } }; static GQuark need_sms[2] = {0, 0}; + static GQuark depends_chat_state[2] = {0, 0}; if (G_LIKELY (features[0].name != 0)) return features; @@ -810,6 +854,15 @@ tp_text_channel_list_features (TpProxyClass *cls G_GNUC_UNUSED) need_sms[0] = TP_IFACE_QUARK_CHANNEL_INTERFACE_SMS; features[FEAT_SMS].interfaces_needed = need_sms; + G_GNUC_BEGIN_IGNORE_DEPRECATIONS + features[FEAT_CHAT_STATES].name = + TP_TEXT_CHANNEL_FEATURE_CHAT_STATES; + features[FEAT_CHAT_STATES].prepare_async = + tp_text_channel_prepare_chat_states_async; + depends_chat_state[0] = TP_CHANNEL_FEATURE_CHAT_STATES; + features[FEAT_CHAT_STATES].depends_on = depends_chat_state; + G_GNUC_END_IGNORE_DEPRECATIONS + /* assert that the terminator at the end is there */ g_assert (features[N_FEAT].name == 0); @@ -1009,6 +1062,26 @@ tp_text_channel_class_init (TpTextChannelClass *klass) 3, TP_TYPE_SIGNALLED_MESSAGE, G_TYPE_UINT, G_TYPE_STRING); g_type_class_add_private (gobject_class, sizeof (TpTextChannelPrivate)); + + /** + * TpTextChannel::contact-chat-state-changed: + * @self: a channel + * @contact: a #TpContact for the local user or another contact + * @state: the new #TpChannelChatState for the contact + * + * Emitted when a contact's chat state changes after tp_proxy_prepare_async() + * has finished preparing features %TP_TEXT_CHANNEL_FEATURE_CHAT_STATES, + * %TP_CHANNEL_FEATURE_GROUP and %TP_CHANNEL_FEATURE_CONTACTS. + * + * Since: 0.19.0 + */ + signals[SIG_CONTACT_CHAT_STATE_CHANGED] = g_signal_new ( + "contact-chat-state-changed", + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, + 0, + NULL, NULL, NULL, + G_TYPE_NONE, 2, TP_TYPE_CONTACT, G_TYPE_UINT); } static void @@ -1478,6 +1551,53 @@ tp_text_channel_ack_message_finish (TpTextChannel *self, _tp_implement_finish_void (self, tp_text_channel_ack_message_async) } +/** + * TP_TEXT_CHANNEL_FEATURE_CHAT_STATES: + * + * Expands to a call to a function that returns a quark representing the + * chat states feature on a #TpTextChannel. + * + * When this feature is prepared, tp_text_channel_get_chat_state() and the + * #TpTextChannel::contact-chat-state-changed signal become useful. + * + * One can ask for a feature to be prepared using the + * tp_proxy_prepare_async() function, and waiting for it to callback. + * + * Since: 0.19.0 + */ + +GQuark +tp_text_channel_get_feature_quark_chat_states (void) +{ + return g_quark_from_static_string ("tp-text-channel-feature-chat-states"); +} + +/** + * tp_text_channel_get_chat_state: + * @self: a channel + * @contact: a #TpContact + * + * Return the chat state for the given contact. If tp_proxy_is_prepared() + * would return %FALSE for the feature %TP_TEXT_CHANNEL_FEATURE_CHAT_STATES, + * the result will always be %TP_CHANNEL_CHAT_STATE_INACTIVE. + * + * Returns: the chat state for @contact, or %TP_CHANNEL_CHAT_STATE_INACTIVE + * if their chat state is not known + * Since: 0.19.0 + */ +TpChannelChatState +tp_text_channel_get_chat_state (TpTextChannel *self, + TpContact *contact) +{ + g_return_val_if_fail (TP_IS_TEXT_CHANNEL (self), 0); + + /* Use the deprecated function internally to avoid duplicated introspection */ + G_GNUC_BEGIN_IGNORE_DEPRECATIONS + return tp_channel_get_chat_state ((TpChannel *) self, + tp_contact_get_handle (contact)); + G_GNUC_END_IGNORE_DEPRECATIONS +} + static void set_chat_state_cb (TpChannel *proxy, const GError *error, diff --git a/telepathy-glib/text-channel.h b/telepathy-glib/text-channel.h index beb5e718a..828def48d 100644 --- a/telepathy-glib/text-channel.h +++ b/telepathy-glib/text-channel.h @@ -121,6 +121,15 @@ gboolean tp_text_channel_ack_all_pending_messages_finish (TpTextChannel *self, GAsyncResult *result, GError **error); +#define TP_TEXT_CHANNEL_FEATURE_CHAT_STATES \ + tp_text_channel_get_feature_quark_chat_states () +_TP_AVAILABLE_IN_0_20 +GQuark tp_text_channel_get_feature_quark_chat_states (void) G_GNUC_CONST; + +_TP_AVAILABLE_IN_0_20 +TpChannelChatState tp_text_channel_get_chat_state (TpTextChannel *self, + TpContact *contact); + void tp_text_channel_set_chat_state_async (TpTextChannel *self, TpChannelChatState state, GAsyncReadyCallback callback, diff --git a/telepathy-glib/tls-certificate-rejection-internal.h b/telepathy-glib/tls-certificate-rejection-internal.h new file mode 100644 index 000000000..f4f747984 --- /dev/null +++ b/telepathy-glib/tls-certificate-rejection-internal.h @@ -0,0 +1,30 @@ +/* + * tls-certificate-rejection-internal.h + * + * Copyright (C) 2012 Collabora Ltd. <http://www.collabora.co.uk/> + * + * 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_TLS_CERTIFICATE_REJECTION_INTERNAL_H__ +#define __TP_TLS_CERTIFICATE_REJECTION_INTERNAL_H__ + +TpTLSCertificateRejection * _tp_tls_certificate_rejection_new ( + GError *error, + TpTLSCertificateRejectReason reason, + const gchar *dbus_error, + GVariant *details); + +#endif diff --git a/telepathy-glib/tls-certificate-rejection.c b/telepathy-glib/tls-certificate-rejection.c new file mode 100644 index 000000000..173234602 --- /dev/null +++ b/telepathy-glib/tls-certificate-rejection.c @@ -0,0 +1,339 @@ +/* + * tls-certificate-rejection.c + * + * Copyright (C) 2012 Collabora Ltd. <http://www.collabora.co.uk/> + * + * 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 + */ + + +#include "config.h" + +#include "tls-certificate-rejection.h" +#include "tls-certificate-rejection-internal.h" + +/** + * SECTION: tls-certificate-rejection + * @title: TpTLSCertificateRejection + * @short_description: a certificate rejection + * + * TpTLSCertificateRejection is a small object used by + * #TpTLSCertificate to represent the rejection of a + * certificate. + */ + +/** + * TpTLSCertificateRejection: + * + * Data structure representing a #TpTLSCertificateRejection. + * + * Since: 0.19.0 + */ + +/** + * TpTLSCertificateRejectionClass: + * + * The class of a #TpTLSCertificateRejection. + * + * Since: 0.19.0 + */ + +G_DEFINE_TYPE (TpTLSCertificateRejection, tp_tls_certificate_rejection, + G_TYPE_OBJECT) + +enum +{ + PROP_REASON = 1, + PROP_DBUS_ERROR, + PROP_DETAILS, + PROP_ERROR, + N_PROPS +}; + +struct _TpTLSCertificateRejectionPriv { + TpTLSCertificateRejectReason reason; + gchar *dbus_error; + GVariant *details; + GError *error; +}; + +static void +tp_tls_certificate_rejection_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + TpTLSCertificateRejection *self = TP_TLS_CERTIFICATE_REJECTION (object); + + switch (property_id) + { + case PROP_REASON: + g_value_set_uint (value, self->priv->reason); + break; + case PROP_DBUS_ERROR: + g_value_set_string (value, self->priv->dbus_error); + break; + case PROP_DETAILS: + g_value_set_variant (value, self->priv->details); + break; + case PROP_ERROR: + g_value_set_boxed (value, self->priv->error); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +tp_tls_certificate_rejection_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + TpTLSCertificateRejection *self = TP_TLS_CERTIFICATE_REJECTION (object); + + switch (property_id) + { + case PROP_REASON: + self->priv->reason = g_value_get_uint (value); + break; + case PROP_DBUS_ERROR: + g_assert (self->priv->dbus_error == NULL); /* construct only */ + self->priv->dbus_error = g_value_dup_string (value); + break; + case PROP_DETAILS: + self->priv->details = g_value_dup_variant (value); + break; + case PROP_ERROR: + self->priv->error = g_value_dup_boxed (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +tp_tls_certificate_rejection_dispose (GObject *object) +{ + TpTLSCertificateRejection *self = TP_TLS_CERTIFICATE_REJECTION (object); + void (*chain_up) (GObject *) = + ((GObjectClass *) tp_tls_certificate_rejection_parent_class)->dispose; + + g_variant_unref (self->priv->details); + + if (chain_up != NULL) + chain_up (object); +} + +static void +tp_tls_certificate_rejection_finalize (GObject *object) +{ + TpTLSCertificateRejection *self = TP_TLS_CERTIFICATE_REJECTION (object); + void (*chain_up) (GObject *) = + ((GObjectClass *) tp_tls_certificate_rejection_parent_class)->finalize; + + g_free (self->priv->dbus_error); + + if (chain_up != NULL) + chain_up (object); +} + +static void +tp_tls_certificate_rejection_class_init ( + TpTLSCertificateRejectionClass *klass) +{ + GObjectClass *oclass = G_OBJECT_CLASS (klass); + GParamSpec *spec; + + oclass->get_property = tp_tls_certificate_rejection_get_property; + oclass->set_property = tp_tls_certificate_rejection_set_property; + oclass->dispose = tp_tls_certificate_rejection_dispose; + oclass->finalize = tp_tls_certificate_rejection_finalize; + + /** + * TpTLSCertificateRejection:reason: + * + * #TpTLSCertificateRejectReason representing the reason of the rejection + * + * Since: 0.19.0 + */ + spec = g_param_spec_uint ("reason", "reason", + "TpTLSCertificateRejectReason", + TP_TLS_CERTIFICATE_REJECT_REASON_UNKNOWN, + TP_NUM_TLS_CERTIFICATE_REJECT_REASONS, + TP_TLS_CERTIFICATE_REJECT_REASON_UNKNOWN, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (oclass, PROP_REASON, spec); + + /** + * TpTLSCertificateRejection:dbus-error: + * + * The D-Bus error name of the rejection + * + * Since: 0.19.0 + */ + spec = g_param_spec_string ("dbus-error", "dbus-error", + "DBus error", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (oclass, PROP_DBUS_ERROR, spec); + + /** + * TpTLSCertificateRejection:details: + * + * A #G_VARIANT_TYPE_VARDICT containing the details of the rejection + * + * Since: 0.19.0 + */ + spec = g_param_spec_variant ("details", "details", + "GVariant", + G_VARIANT_TYPE_VARDICT, NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (oclass, PROP_DETAILS, spec); + + /** + * TpTLSCertificateRejection:error: + * + * a #GError (likely to be in the %TP_ERROR domain) indicating the reason + * of the rejection + * + * Since: 0.19.0 + */ + spec = g_param_spec_boxed ("error", "error", + "GError", + G_TYPE_ERROR, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (oclass, PROP_ERROR, spec); + + g_type_class_add_private (klass, sizeof (TpTLSCertificateRejectionPriv)); +} + +static void +tp_tls_certificate_rejection_init (TpTLSCertificateRejection *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, + TP_TYPE_TLS_CERTIFICATE_REJECTION, TpTLSCertificateRejectionPriv); +} + +/* @details is sinked if it's a floating reference */ +TpTLSCertificateRejection * +_tp_tls_certificate_rejection_new ( + GError *error, + TpTLSCertificateRejectReason reason, + const gchar *dbus_error, + GVariant *details) +{ + TpTLSCertificateRejection *ret; + + g_variant_ref_sink (details); + + ret = g_object_new (TP_TYPE_TLS_CERTIFICATE_REJECTION, + "error", error, + "reason", reason, + "dbus-error", dbus_error, + "details", details, + NULL); + + g_variant_unref (details); + return ret; +} + +/** + * tp_tls_certificate_rejection_get_error: + * @self: a #TpTLSCertificateRejection + * + * Return the #TpTLSCertificateRejection:error property + * + * Returns: the value of #TpTLSCertificateRejection:error property + * + * Since: 0.19.0 + */ +const GError * +tp_tls_certificate_rejection_get_error (TpTLSCertificateRejection *self) +{ + return self->priv->error; +} + +/** + * tp_tls_certificate_rejection_get_reason: + * @self: a #TpTLSCertificateRejection + * + * Return the #TpTLSCertificateRejection:reason property + * + * Returns: the value of #TpTLSCertificateRejection:reason property + * + * Since: 0.19.0 + */ +TpTLSCertificateRejectReason +tp_tls_certificate_rejection_get_reason (TpTLSCertificateRejection *self) +{ + return self->priv->reason; +} + +/** + * tp_tls_certificate_rejection_get_dbus_error: + * @self: a #TpTLSCertificateRejection + * + * Return the #TpTLSCertificateRejection:dbus-error property + * + * Returns: the value of #TpTLSCertificateRejection:dbus-error property + * + * Since: 0.19.0 + */ +const gchar * +tp_tls_certificate_rejection_get_dbus_error (TpTLSCertificateRejection *self) +{ + return self->priv->dbus_error; +} + +/** + * tp_tls_certificate_rejection_get_details: + * @self: a #TpTLSCertificateRejection + * + * Return the #TpTLSCertificateRejection:details property + * + * Returns: the value of #TpTLSCertificateRejection:details property + * + * Since: 0.19.0 + */ +GVariant * +tp_tls_certificate_rejection_get_details (TpTLSCertificateRejection *self) +{ + return self->priv->details; +} + +/** + * tp_tls_certificate_rejection_raise_error: + * @self: a #TpTLSCertificateRejection + * @error: (out) (allow-none) (transfer full): a #GError to fill + * + * Convenient function to raise the #TpTLSCertificateRejection:error + * property in language binding supporting this feature. + * + * Returns: %FALSE + * + * Since: 0.19.0 + */ +gboolean +tp_tls_certificate_rejection_raise_error (TpTLSCertificateRejection *self, + GError **error) +{ + if (error != NULL) + *error = g_error_copy (self->priv->error); + + return FALSE; +} diff --git a/telepathy-glib/tls-certificate-rejection.h b/telepathy-glib/tls-certificate-rejection.h new file mode 100644 index 000000000..50f5199a9 --- /dev/null +++ b/telepathy-glib/tls-certificate-rejection.h @@ -0,0 +1,92 @@ +/* + * tls-certificate-rejection.h + * + * Copyright (C) 2012 Collabora Ltd. <http://www.collabora.co.uk/> + * + * 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_TLS_CERTIFICATE_REJECTION_H__ +#define __TP_TLS_CERTIFICATE_REJECTION_H__ + +#include <glib-object.h> + +#include <telepathy-glib/defs.h> +#include <telepathy-glib/enums.h> + +G_BEGIN_DECLS + +typedef struct _TpTLSCertificateRejection TpTLSCertificateRejection; +typedef struct _TpTLSCertificateRejectionClass TpTLSCertificateRejectionClass; +typedef struct _TpTLSCertificateRejectionPriv TpTLSCertificateRejectionPriv; + +struct _TpTLSCertificateRejectionClass { + /*<private>*/ + GObjectClass parent_class; +}; + +struct _TpTLSCertificateRejection { + /*<private>*/ + GObject parent; + TpTLSCertificateRejectionPriv *priv; +}; + +_TP_AVAILABLE_IN_0_20 +GType tp_tls_certificate_rejection_get_type (void); + +/* TYPE MACROS */ +#define TP_TYPE_TLS_CERTIFICATE_REJECTION \ + (tp_tls_certificate_rejection_get_type ()) +#define TP_TLS_CERTIFICATE_REJECTION(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), \ + TP_TYPE_TLS_CERTIFICATE_REJECTION, \ + TpTLSCertificateRejection)) +#define TP_TLS_CERTIFICATE_REJECTION_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), \ + TP_TYPE_TLS_CERTIFICATE_REJECTION, \ + TpTLSCertificateRejectionClass)) +#define TP_IS_TLS_CERTIFICATE_REJECTION(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), \ + TP_TYPE_TLS_CERTIFICATE_REJECTION)) +#define TP_IS_TLS_CERTIFICATE_REJECTION_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), \ + TP_TYPE_TLS_CERTIFICATE_REJECTION)) +#define TP_TLS_CERTIFICATE_REJECTION_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + TP_TYPE_TLS_CERTIFICATE_REJECTION, \ + TpTLSCertificateRejectionClass)) + +_TP_AVAILABLE_IN_0_20 +const GError * tp_tls_certificate_rejection_get_error ( + TpTLSCertificateRejection *self); +_TP_AVAILABLE_IN_0_20 +TpTLSCertificateRejectReason tp_tls_certificate_rejection_get_reason ( + TpTLSCertificateRejection *self); +_TP_AVAILABLE_IN_0_20 +const gchar * tp_tls_certificate_rejection_get_dbus_error ( + TpTLSCertificateRejection *self); +_TP_AVAILABLE_IN_0_20 +GVariant * tp_tls_certificate_rejection_get_details ( + TpTLSCertificateRejection *self); + +_TP_AVAILABLE_IN_0_20 +gboolean tp_tls_certificate_rejection_raise_error ( + TpTLSCertificateRejection *self, + GError **error); + +G_END_DECLS + +#endif /* #ifndef __TP_TLS_CERTIFICATE_REJECTION_H__*/ diff --git a/telepathy-glib/tls-certificate.c b/telepathy-glib/tls-certificate.c new file mode 100644 index 000000000..46374c127 --- /dev/null +++ b/telepathy-glib/tls-certificate.c @@ -0,0 +1,923 @@ +/* + * TpTLSCertificate - a TpProxy for TLS certificates + * Copyright © 2010 Collabora Ltd. + * + * Based on EmpathyTLSCertificate: + * @author Cosimo Cecchi <cosimo.cecchi@collabora.co.uk> + * + * 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 + */ + +#include <config.h> +#include "telepathy-glib/tls-certificate.h" + +#include <glib/gstdio.h> + +#include <telepathy-glib/cli-misc.h> +#include <telepathy-glib/dbus.h> +#include <telepathy-glib/dbus-internal.h> +#include <telepathy-glib/enums.h> +#include <telepathy-glib/gtypes.h> +#include <telepathy-glib/interfaces.h> +#include <telepathy-glib/proxy-internal.h> +#include <telepathy-glib/proxy-subclass.h> +#include <telepathy-glib/util.h> +#include <telepathy-glib/util-internal.h> +#include <telepathy-glib/tls-certificate-rejection-internal.h> + +#define DEBUG_FLAG TP_DEBUG_TLS +#include "debug-internal.h" + +/** + * SECTION:tls-certificate + * @title: TpTLSCertificate + * @short_description: proxy object for a server or peer's TLS certificate + * + * #TpTLSCertificate is a #TpProxy subclass for TLSCertificate objects, + * used in Channel.Type.ServerTLSConnection. + * + * Since: 0.19.0 + */ + +/** + * TpTLSCertificate: + * + * A #TpProxy subclass representing a server or peer's TLS certificate + * being presented for acceptance/rejection. + * + * Since: 0.19.0 + */ + +/** + * TpTLSCertificateClass: + * + * The class of a #TpTLSCertificate. + * + * Since: 0.19.0 + */ + +enum { + /* proxy properties */ + PROP_CERT_TYPE = 1, + PROP_CERT_DATA, + PROP_STATE, + PROP_PARENT, + N_PROPS +}; + +struct _TpTLSCertificatePrivate { + TpProxy *parent; + + /* TLSCertificate properties */ + gchar *cert_type; + GPtrArray *cert_data; + TpTLSCertificateState state; + /* array of TpTLSCertificateRejection received from the CM */ + GPtrArray *rejections; + /* GPtrArray of TP_STRUCT_TYPE_TLS_CERTIFICATE_REJECTION to send to CM */ + GPtrArray *pending_rejections; +}; + +G_DEFINE_TYPE (TpTLSCertificate, tp_tls_certificate, + TP_TYPE_PROXY) + +/** + * TP_TLS_CERTIFICATE_FEATURE_CORE: + * + * Expands to a call to a function that returns a quark representing the + * core functionality of a #TpTLSCertificate. + * + * When this feature is prepared, the basic properties of the + * object have been retrieved and are available for use: + * + * <itemizedlist> + * <listitem>#TpTLSCertificate:cert-type</listitem> + * <listitem>#TpTLSCertificate:cert-data</listitem> + * <listitem>#TpTLSCertificate:state</listitem> + * </itemizedlist> + * + * In addition, #GObject::notify::state will be emitted if the state changes. + * + * One can ask for a feature to be prepared using the + * tp_proxy_prepare_async() function, and waiting for it to callback. + * + * Since: 0.19.0 + */ + +GQuark +tp_tls_certificate_get_feature_quark_core (void) +{ + return g_quark_from_static_string ("tp-tls-certificate-feature-core"); +} + +static void +tp_tls_certificate_accepted_cb (TpTLSCertificate *self, + gpointer unused G_GNUC_UNUSED, + GObject *unused_object G_GNUC_UNUSED) +{ + tp_clear_pointer (&self->priv->rejections, g_ptr_array_unref); + self->priv->state = TP_TLS_CERTIFICATE_STATE_ACCEPTED; + g_object_notify ((GObject *) self, "state"); +} + +static void +tp_tls_certificate_rejected_cb (TpTLSCertificate *self, + const GPtrArray *rejections, + gpointer unused G_GNUC_UNUSED, + GObject *unused_object G_GNUC_UNUSED) +{ + self->priv->state = TP_TLS_CERTIFICATE_STATE_REJECTED; + + tp_clear_pointer (&self->priv->rejections, g_ptr_array_unref); + self->priv->rejections = g_ptr_array_new_with_free_func (g_object_unref); + + if (rejections == NULL || rejections->len == 0) + { + TpTLSCertificateRejection *rej; + GVariantBuilder builder; + GError *error = g_error_new_literal (TP_ERROR, TP_ERROR_CERT_INVALID, + "Rejected, no reason given"); + + g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}")); + + rej = _tp_tls_certificate_rejection_new (error, + TP_TLS_CERTIFICATE_REJECT_REASON_UNKNOWN, TP_ERROR_STR_CERT_INVALID, + g_variant_builder_end (&builder)); + + g_ptr_array_add (self->priv->rejections, rej); + g_error_free (error); + } + else + { + guint i; + + for (i = 0; i < rejections->len; i++) + { + TpTLSCertificateRejection *rej; + GValueArray *va = g_ptr_array_index (rejections, i); + const gchar *error_name; + const GHashTable *details; + TpTLSCertificateRejectReason reason; + GError *error = NULL; + GVariant *vardict; + + tp_value_array_unpack (va, 3, + &reason, + &error_name, + &details); + + tp_proxy_dbus_error_to_gerror (self, error_name, + tp_asv_get_string (details, "debug-message"), &error); + + vardict = _tp_asv_to_vardict (details); + + rej = _tp_tls_certificate_rejection_new (error, + reason, error_name, vardict); + + g_ptr_array_add (self->priv->rejections, rej); + + g_error_free (error); + g_variant_unref (vardict); + } + } + + g_object_notify ((GObject *) self, "state"); +} + +static void +tls_certificate_got_all_cb (TpProxy *proxy, + GHashTable *properties, + const GError *error, + gpointer user_data, + GObject *weak_object) +{ + GPtrArray *cert_data; + TpTLSCertificate *self = TP_TLS_CERTIFICATE (proxy); + guint state; + guint i; + + if (error != NULL) + { + tp_proxy_invalidate (proxy, error); + return; + } + + self->priv->cert_type = g_strdup (tp_asv_get_string (properties, + "CertificateType")); + + state = tp_asv_get_uint32 (properties, "State", NULL); + + switch (state) + { + case TP_TLS_CERTIFICATE_STATE_PENDING: + break; + + case TP_TLS_CERTIFICATE_STATE_ACCEPTED: + tp_tls_certificate_accepted_cb (self, NULL, NULL); + break; + + case TP_TLS_CERTIFICATE_STATE_REJECTED: + tp_tls_certificate_rejected_cb (self, + tp_asv_get_boxed (properties, "Rejections", + TP_ARRAY_TYPE_TLS_CERTIFICATE_REJECTION_LIST), + NULL, NULL); + break; + + default: + { + GError e = { TP_DBUS_ERRORS, TP_DBUS_ERROR_INCONSISTENT, + "Invalid State" }; + + DEBUG ("Invalid state '%u' on %s", state, + tp_proxy_get_object_path (self)); + tp_proxy_invalidate (proxy, &e); + } + } + + cert_data = tp_asv_get_boxed (properties, "CertificateChainData", + TP_ARRAY_TYPE_UCHAR_ARRAY_LIST); + g_assert (cert_data != NULL); + + self->priv->cert_data = g_ptr_array_new_with_free_func ( + (GDestroyNotify) g_bytes_unref); + + for (i = 0; i < cert_data->len; i++) + { + GArray *arr = g_ptr_array_index (cert_data, i); + GBytes *bytes; + + bytes = g_bytes_new (arr->data, arr->len); + g_ptr_array_add (self->priv->cert_data, bytes); + } + + DEBUG ("Got a certificate chain long %u, of type %s", + self->priv->cert_data->len, self->priv->cert_type); + + _tp_proxy_set_feature_prepared (proxy, TP_TLS_CERTIFICATE_FEATURE_CORE, + TRUE); +} + +static void +parent_invalidated_cb (TpProxy *parent, + guint domain, + gint code, + gchar *message, + TpTLSCertificate *self) +{ + GError e = { domain, code, message }; + + tp_clear_object (&self->priv->parent); + + tp_proxy_invalidate ((TpProxy *) self, &e); + g_object_notify ((GObject *) self, "parent"); +} + +static void +tp_tls_certificate_constructed (GObject *object) +{ + TpTLSCertificate *self = TP_TLS_CERTIFICATE (object); + void (*constructed) (GObject *) = + G_OBJECT_CLASS (tp_tls_certificate_parent_class)->constructed; + + if (constructed != NULL) + constructed (object); + + g_return_if_fail (self->priv->parent == NULL || + TP_IS_CHANNEL (self->priv->parent) || + TP_IS_CONNECTION (self->priv->parent)); + + if (self->priv->parent != NULL) + { + if (self->priv->parent->invalidated != NULL) + { + GError *invalidated = self->priv->parent->invalidated; + + parent_invalidated_cb (self->priv->parent, invalidated->domain, + invalidated->code, invalidated->message, self); + } + else + { + tp_g_signal_connect_object (self->priv->parent, + "invalidated", G_CALLBACK (parent_invalidated_cb), self, 0); + } + } + + tp_cli_authentication_tls_certificate_connect_to_accepted (self, + tp_tls_certificate_accepted_cb, NULL, NULL, NULL, NULL); + tp_cli_authentication_tls_certificate_connect_to_rejected (self, + tp_tls_certificate_rejected_cb, NULL, NULL, NULL, NULL); + + tp_cli_dbus_properties_call_get_all (self, + -1, TP_IFACE_AUTHENTICATION_TLS_CERTIFICATE, + tls_certificate_got_all_cb, NULL, NULL, NULL); +} + +static void +tp_tls_certificate_finalize (GObject *object) +{ + TpTLSCertificate *self = TP_TLS_CERTIFICATE (object); + TpTLSCertificatePrivate *priv = self->priv; + + DEBUG ("%p", object); + + tp_clear_pointer (&self->priv->rejections, g_ptr_array_unref); + g_free (priv->cert_type); + if (priv->cert_data != NULL) + g_ptr_array_unref (priv->cert_data); + tp_clear_boxed (TP_ARRAY_TYPE_TLS_CERTIFICATE_REJECTION_LIST, + &self->priv->pending_rejections); + + G_OBJECT_CLASS (tp_tls_certificate_parent_class)->finalize (object); +} + +static void +tp_tls_certificate_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + TpTLSCertificate *self = TP_TLS_CERTIFICATE (object); + TpTLSCertificatePrivate *priv = self->priv; + + switch (property_id) + { + case PROP_CERT_TYPE: + g_value_set_string (value, priv->cert_type); + break; + + case PROP_CERT_DATA: + g_value_set_boxed (value, priv->cert_data); + break; + + case PROP_STATE: + g_value_set_uint (value, priv->state); + break; + + case PROP_PARENT: + g_value_set_object (value, priv->parent); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +tp_tls_certificate_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + TpTLSCertificate *self = TP_TLS_CERTIFICATE (object); + + switch (property_id) + { + case PROP_PARENT: + self->priv->parent = TP_PROXY (g_value_dup_object (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +tp_tls_certificate_init (TpTLSCertificate *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, + TP_TYPE_TLS_CERTIFICATE, TpTLSCertificatePrivate); +} + +enum { + FEAT_CORE, + N_FEAT +}; + +static const TpProxyFeature * +tp_tls_certificate_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_TLS_CERTIFICATE_FEATURE_CORE; + features[FEAT_CORE].core = TRUE; + + g_assert (features[N_FEAT].name == 0); + return features; +} + +static void +tp_tls_certificate_class_init (TpTLSCertificateClass *klass) +{ + GParamSpec *pspec; + GObjectClass *oclass = G_OBJECT_CLASS (klass); + TpProxyClass *pclass = TP_PROXY_CLASS (klass); + + tp_tls_certificate_init_known_interfaces (); + + oclass->get_property = tp_tls_certificate_get_property; + oclass->set_property = tp_tls_certificate_set_property; + oclass->constructed = tp_tls_certificate_constructed; + oclass->finalize = tp_tls_certificate_finalize; + + pclass->interface = TP_IFACE_QUARK_AUTHENTICATION_TLS_CERTIFICATE; + pclass->must_have_unique_name = TRUE; + pclass->list_features = tp_tls_certificate_list_features; + + g_type_class_add_private (klass, sizeof (TpTLSCertificatePrivate)); + + /** + * TpTLSCertificate:cert-type: + * + * The type of the certificate, typically either "x509" or "pgp". + * + * Since: 0.19.0 + */ + pspec = g_param_spec_string ("cert-type", "Certificate type", + "The type of this certificate.", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (oclass, PROP_CERT_TYPE, pspec); + + /** + * TpTLSCertificate:cert-data: + * + * The raw data of the certificate or certificate chain, represented + * as a #GPtrArray of #GBytes. It should be interpreted + * according to #TpTLSCertificate:cert-type. + * + * The first certificate in this array is the server's certificate, + * followed by its issuer, followed by the issuer's issuer and so on. + * + * For "x509" certificates, each certificate is an X.509 certificate in + * binary (DER) format. + * + * For "pgp" certificates, each certificate is a binary OpenPGP key. + * + * Since: 0.19.0 + */ + pspec = g_param_spec_boxed ("cert-data", "Certificate chain data", + "The raw DER-encoded certificate chain data.", + G_TYPE_PTR_ARRAY, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (oclass, PROP_CERT_DATA, pspec); + + /** + * TpTLSCertificate:state: + * + * The state of this TLS certificate as a #TpTLSCertificateState, + * initially %TP_TLS_CERTIFICATE_STATE_PENDING. + * + * #GObject::notify::state will be emitted when this changes. + * + * Since: 0.19.0 + */ + pspec = g_param_spec_uint ("state", "State", + "The state of this certificate.", + 0, TP_NUM_TLS_CERTIFICATE_STATES - 1, TP_TLS_CERTIFICATE_STATE_PENDING, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (oclass, PROP_STATE, pspec); + + /** + * TpTLSCertificate:parent: + * + * A #TpConnection or #TpChannel which owns this TLS certificate. If the + * parent object is invalidated, the certificate is also invalidated, and + * this property is set to %NULL. + * + * Since: 0.19.0 + */ + pspec = g_param_spec_object ("parent", "Parent", + "The TpConnection or TpChannel to which this belongs", TP_TYPE_PROXY, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (oclass, PROP_PARENT, pspec); +} + +static void +cert_proxy_accept_cb (TpTLSCertificate *self, + const GError *error, + gpointer user_data, + GObject *unused_object G_GNUC_UNUSED) +{ + GSimpleAsyncResult *accept_result = user_data; + + DEBUG ("Callback for accept(), error %p", error); + + if (error != NULL) + { + DEBUG ("Error was %s", error->message); + g_simple_async_result_set_from_error (accept_result, error); + } + + g_simple_async_result_complete (accept_result); +} + +static void +cert_proxy_reject_cb (TpTLSCertificate *self, + const GError *error, + gpointer user_data, + GObject *unused_object G_GNUC_UNUSED) +{ + GSimpleAsyncResult *reject_result = user_data; + + DEBUG ("Callback for reject(), error %p", error); + + if (error != NULL) + { + DEBUG ("Error was %s", error->message); + g_simple_async_result_set_from_error (reject_result, error); + } + + g_simple_async_result_complete (reject_result); +} + +static const gchar * +reject_reason_get_dbus_error (TpTLSCertificateRejectReason reason) +{ + const gchar *retval = NULL; + + switch (reason) + { +#define EASY_CASE(x) \ + case TP_TLS_CERTIFICATE_REJECT_REASON_ ## x: \ + retval = tp_error_get_dbus_name (TP_ERROR_CERT_ ## x); \ + break + EASY_CASE (UNTRUSTED); + EASY_CASE (EXPIRED); + EASY_CASE (NOT_ACTIVATED); + EASY_CASE (FINGERPRINT_MISMATCH); + EASY_CASE (HOSTNAME_MISMATCH); + EASY_CASE (SELF_SIGNED); + EASY_CASE (REVOKED); + EASY_CASE (INSECURE); + EASY_CASE (LIMIT_EXCEEDED); +#undef EASY_CASE + + case TP_TLS_CERTIFICATE_REJECT_REASON_UNKNOWN: + default: + retval = tp_error_get_dbus_name (TP_ERROR_CERT_INVALID); + break; + } + + return retval; +} + +/** + * tp_tls_certificate_new: + * @conn_or_chan: a #TpConnection or #TpChannel parent for this object, whose + * invalidation will also result in invalidation of the returned object + * @object_path: the object path of this TLS certificate + * @error: a #GError used to return an error if %NULL is returned, or %NULL + * + * <!-- --> + * + * Returns: (transfer full): a new TLS certificate proxy. Prepare the + * feature %TP_TLS_CERTIFICATE_FEATURE_CORE to make it useful. + * Since: 0.19.0 + */ +TpTLSCertificate * +tp_tls_certificate_new (TpProxy *conn_or_chan, + const gchar *object_path, + GError **error) +{ + TpTLSCertificate *retval = NULL; + + g_return_val_if_fail (TP_IS_CONNECTION (conn_or_chan) || + TP_IS_CHANNEL (conn_or_chan), NULL); + + if (!tp_dbus_check_valid_object_path (object_path, error)) + goto finally; + + retval = g_object_new (TP_TYPE_TLS_CERTIFICATE, + "parent", conn_or_chan, + "dbus-daemon", conn_or_chan->dbus_daemon, + "bus-name", conn_or_chan->bus_name, + "object-path", object_path, + NULL); + +finally: + return retval; +} + +/** + * tp_tls_certificate_accept_async: + * @self: a TLS certificate + * @callback: called on success or failure + * @user_data: user data for the callback + * + * Accept this certificate, asynchronously. In or after @callback, + * you may call tp_tls_certificate_accept_finish() to check the result. + * + * #GObject::notify::state will also be emitted when the connection manager + * signals that the certificate has been accepted. + * Since: 0.19.0 + */ +void +tp_tls_certificate_accept_async (TpTLSCertificate *self, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *accept_result; + + g_assert (TP_IS_TLS_CERTIFICATE (self)); + + DEBUG ("Accepting TLS certificate"); + + accept_result = g_simple_async_result_new (G_OBJECT (self), + callback, user_data, tp_tls_certificate_accept_async); + + tp_cli_authentication_tls_certificate_call_accept (self, + -1, cert_proxy_accept_cb, + accept_result, g_object_unref, NULL); +} + +/** + * tp_tls_certificate_accept_finish: + * @self: a TLS certificate + * @result: the result passed to the callback by + * tp_tls_certificate_accept_async() + * @error: used to raise an error if %FALSE is returned + * + * Check the result of tp_tls_certificate_accept_async(). + * + * Returns: %TRUE if acceptance was successful + * Since: 0.19.0 + */ +gboolean +tp_tls_certificate_accept_finish (TpTLSCertificate *self, + GAsyncResult *result, + GError **error) +{ + _tp_implement_finish_void (self, tp_tls_certificate_accept_async) +} + +/** + * tp_tls_certificate_add_rejection: + * @self: a TLS certificate + * @reason: the reason for rejection + * @dbus_error: a D-Bus error name such as %TP_ERROR_STR_CERT_REVOKED, or + * %NULL to derive one from @reason + * @details: (transfer none) (allow-none): a variant of type + * %G_VARIANT_TYPE_VARDICT containing the details of the rejection, or %NULL + * + * Add a pending reason for rejection. The first call to this method is + * considered "most important". After calling this method as many times + * as are required, call tp_tls_certificate_reject_async() to reject the + * certificate. + * + * If @details is a floating reference (see g_variant_ref_sink()), + * ownership of @details is taken by this function. This means + * 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.19.0 + */ +void +tp_tls_certificate_add_rejection (TpTLSCertificate *self, + TpTLSCertificateRejectReason reason, + const gchar *dbus_error, + GVariant *details) +{ + GValueArray *rejection; + GHashTable *hash; + + g_return_if_fail (dbus_error == NULL || + tp_dbus_check_valid_interface_name (dbus_error, NULL)); + g_return_if_fail (details == NULL || + g_variant_is_of_type (details, G_VARIANT_TYPE_VARDICT)); + + if (self->priv->pending_rejections == NULL) + self->priv->pending_rejections = g_ptr_array_new (); + + if (dbus_error == NULL) + dbus_error = reject_reason_get_dbus_error (reason); + + if (details == NULL) + { + hash = g_hash_table_new (NULL, NULL); + } + else + { + hash = _tp_asv_from_vardict (details); + g_variant_ref_sink (details); + } + + rejection = tp_value_array_build (3, + G_TYPE_UINT, reason, + G_TYPE_STRING, dbus_error, + TP_HASH_TYPE_STRING_VARIANT_MAP, hash, + NULL); + + g_ptr_array_add (self->priv->pending_rejections, rejection); + + g_hash_table_unref (hash); + + if (details != NULL) + g_variant_unref (details); +} + +/** + * tp_tls_certificate_reject_async: + * @self: a TLS certificate + * @callback: called on success or failure + * @user_data: user data for the callback + * + * Reject this certificate, asynchronously. + * + * Before calling this method, you must call + * tp_tls_certificate_add_rejection() at least once, to set the reason(s) + * for rejection (for instance, a certificate might be both self-signed and + * expired). + * + * In or after @callback, + * you may call tp_tls_certificate_reject_finish() to check the result. + * + * #GObject::notify::state will also be emitted when the connection manager + * signals that the certificate has been rejected. + * Since: 0.19.0 + */ +void +tp_tls_certificate_reject_async (TpTLSCertificate *self, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *reject_result; + + g_return_if_fail (TP_IS_TLS_CERTIFICATE (self)); + g_return_if_fail (self->priv->pending_rejections != NULL); + g_return_if_fail (self->priv->pending_rejections->len >= 1); + + reject_result = g_simple_async_result_new (G_OBJECT (self), + callback, user_data, tp_tls_certificate_reject_async); + + tp_cli_authentication_tls_certificate_call_reject (self, + -1, self->priv->pending_rejections, cert_proxy_reject_cb, + reject_result, g_object_unref, NULL); + + tp_clear_boxed (TP_ARRAY_TYPE_TLS_CERTIFICATE_REJECTION_LIST, + &self->priv->pending_rejections); +} + +/** + * tp_tls_certificate_reject_finish: + * @self: a TLS certificate + * @result: the result passed to the callback by + * tp_tls_certificate_reject_async() + * @error: used to raise an error if %FALSE is returned + * + * Check the result of tp_tls_certificate_reject_async(). + * + * Returns: %TRUE if rejection was successful + * Since: 0.19.0 + */ +gboolean +tp_tls_certificate_reject_finish (TpTLSCertificate *self, + GAsyncResult *result, + GError **error) +{ + _tp_implement_finish_void (self, tp_tls_certificate_reject_async) +} + +#include <telepathy-glib/_gen/tp-cli-tls-cert-body.h> + +/** + * tp_tls_certificate_init_known_interfaces: + * + * Ensure that the known interfaces for TpTLSCertificate have been set up. + * This is done automatically when necessary, but for correct + * overriding of library interfaces by local extensions, you should + * call this function before calling + * tp_proxy_or_subclass_hook_on_interface_add() with first argument + * %TP_TYPE_TLS_CERTIFICATE. + * + * Since: 0.19.0 + */ +void +tp_tls_certificate_init_known_interfaces (void) +{ + static gsize once = 0; + + if (g_once_init_enter (&once)) + { + GType tp_type = TP_TYPE_TLS_CERTIFICATE; + + tp_proxy_init_known_interfaces (); + tp_proxy_or_subclass_hook_on_interface_add (tp_type, + tp_cli_tls_cert_add_signals); + tp_proxy_subclass_add_error_mapping (tp_type, + TP_ERROR_PREFIX, TP_ERROR, TP_TYPE_ERROR); + + g_once_init_leave (&once, 1); + } +} + +/** + * tp_tls_certificate_get_rejection: + * @self: a TLS certificate + * + * If this certificate has been rejected, return a #TpTLSCertificateRejection + * indicating the first rejection reason (by convention, + * the most important). + * + * If you want to list all the things that are wrong with the certificate + * (for instance, it might be self-signed and also have expired) + * you can call tp_tls_certificate_get_nth_rejection(), increasing @n until + * it returns %NULL. + * + * Returns: (transfer none) (allow-none): a #TpTLSCertificateRejection, or %NULL + * Since: 0.19.0 + */ +TpTLSCertificateRejection * +tp_tls_certificate_get_rejection (TpTLSCertificate *self) +{ + return tp_tls_certificate_get_nth_rejection (self, 0); +} + +/** + * tp_tls_certificate_get_nth_rejection: + * @self: a TLS certificate + * @n: the rejection reason to return; if 0, return the same thing as + * tp_tls_certificate_get_detailed_rejection() + * + * If this certificate has been rejected and @n is less than the number of + * rejection reasons, return a #TpTLSCertificateRejection representing the + * @n<!---->th rejection reason (starting from 0). + * + * With @n == 0 this is equivalent to tp_tls_certificate_get_rejection(). + * + * Returns: (transfer none) (allow-none): a #TpTLSCertificateRejection, or %NULL + * Since: 0.19.0 + */ +TpTLSCertificateRejection * +tp_tls_certificate_get_nth_rejection (TpTLSCertificate *self, + guint n) +{ + if (self->priv->rejections == NULL || n >= self->priv->rejections->len) + return NULL; + + return g_ptr_array_index (self->priv->rejections, n); +} + +/** + * tp_tls_certificate_get_cert_type: + * @self: a #TpTLSCertificate + * + * Return the #TpTLSCertificate:cert-type property + * + * Returns: the value of #TpTLSCertificate:cert-type property + * + * Since: 0.19.0 + */ +const gchar * +tp_tls_certificate_get_cert_type (TpTLSCertificate *self) +{ + return self->priv->cert_type; +} + +/** + * tp_tls_certificate_get_cert_data: + * @self: a #TpTLSCertificate + * + * Return the #TpTLSCertificate:cert-data property + * + * Returns: (transfer none) (type GLib.PtrArray) (element-type GLib.Bytes): the value of #TpTLSCertificate:cert-data property + * + * Since: 0.19.0 + */ +GPtrArray * +tp_tls_certificate_get_cert_data (TpTLSCertificate *self) +{ + return self->priv->cert_data; +} + +/** + * tp_tls_certificate_get_state: + * @self: a #TpTLSCertificate + * + * Return the #TpTLSCertificate:state property + * + * Returns: the value of #TpTLSCertificate:state property + * + * Since: 0.19.0 + */ +TpTLSCertificateState +tp_tls_certificate_get_state (TpTLSCertificate *self) +{ + return self->priv->state; +} diff --git a/telepathy-glib/tls-certificate.h b/telepathy-glib/tls-certificate.h new file mode 100644 index 000000000..72b1c018d --- /dev/null +++ b/telepathy-glib/tls-certificate.h @@ -0,0 +1,127 @@ +/* + * TpTLSCertificate - a TpProxy for TLS certificates + * Copyright © 2010 Collabora Ltd. + * + * Based on EmpathyTLSCertificate: + * @author Cosimo Cecchi <cosimo.cecchi@collabora.co.uk> + * + * 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_TLS_CERTIFICATE_H__ +#define __TP_TLS_CERTIFICATE_H__ + +#include <glib-object.h> +#include <gio/gio.h> + +#include <telepathy-glib/channel.h> +#include <telepathy-glib/enums.h> +#include <telepathy-glib/proxy.h> +#include <telepathy-glib/tls-certificate-rejection.h> + +G_BEGIN_DECLS + +typedef struct _TpTLSCertificate TpTLSCertificate; +typedef struct _TpTLSCertificateClass TpTLSCertificateClass; +typedef struct _TpTLSCertificatePrivate TpTLSCertificatePrivate; +typedef struct _TpTLSCertificateClassPrivate TpTLSCertificateClassPrivate; + +struct _TpTLSCertificateClass { + /*<private>*/ + TpProxyClass parent_class; + GCallback _future[3]; + TpTLSCertificateClassPrivate *priv; +}; + +struct _TpTLSCertificate { + /*<private>*/ + TpProxy parent; + TpTLSCertificatePrivate *priv; +}; + +_TP_AVAILABLE_IN_0_20 +GType tp_tls_certificate_get_type (void); + +#define TP_TYPE_TLS_CERTIFICATE \ + (tp_tls_certificate_get_type ()) +#define TP_TLS_CERTIFICATE(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), TP_TYPE_TLS_CERTIFICATE, \ + TpTLSCertificate)) +#define TP_TLS_CERTIFICATE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), TP_TYPE_TLS_CERTIFICATE, \ + TpTLSCertificateClass)) +#define TP_IS_TLS_CERTIFICATE(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TP_TYPE_TLS_CERTIFICATE)) +#define TP_IS_TLS_CERTIFICATE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), TP_TYPE_TLS_CERTIFICATE)) +#define TP_TLS_CERTIFICATE_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), TP_TYPE_TLS_CERTIFICATE, \ + TpTLSCertificateClass)) + +_TP_AVAILABLE_IN_0_20 +GQuark tp_tls_certificate_get_feature_quark_core (void); +#define TP_TLS_CERTIFICATE_FEATURE_CORE \ + (tp_tls_certificate_get_feature_quark_core ()) + +_TP_AVAILABLE_IN_0_20 +TpTLSCertificate *tp_tls_certificate_new (TpProxy *conn_or_chan, + const gchar *object_path, + GError **error); + +_TP_AVAILABLE_IN_0_20 +TpTLSCertificateRejection *tp_tls_certificate_get_rejection ( + TpTLSCertificate *self); + +_TP_AVAILABLE_IN_0_20 +TpTLSCertificateRejection *tp_tls_certificate_get_nth_rejection ( + TpTLSCertificate *self, + guint n); + +_TP_AVAILABLE_IN_0_20 +void tp_tls_certificate_accept_async (TpTLSCertificate *self, + GAsyncReadyCallback callback, + gpointer user_data); +_TP_AVAILABLE_IN_0_20 +gboolean tp_tls_certificate_accept_finish (TpTLSCertificate *self, + GAsyncResult *result, + GError **error); + +_TP_AVAILABLE_IN_0_20 +void tp_tls_certificate_add_rejection (TpTLSCertificate *self, + TpTLSCertificateRejectReason reason, + const gchar *dbus_error, + GVariant *details); +_TP_AVAILABLE_IN_0_20 +void tp_tls_certificate_reject_async (TpTLSCertificate *self, + GAsyncReadyCallback callback, + gpointer user_data); +_TP_AVAILABLE_IN_0_20 +gboolean tp_tls_certificate_reject_finish (TpTLSCertificate *self, + GAsyncResult *result, + GError **error); + +_TP_AVAILABLE_IN_0_20 +void tp_tls_certificate_init_known_interfaces (void); + +_TP_AVAILABLE_IN_0_20 +const gchar * tp_tls_certificate_get_cert_type (TpTLSCertificate *self); +_TP_AVAILABLE_IN_0_20 +GPtrArray * tp_tls_certificate_get_cert_data (TpTLSCertificate *self); +_TP_AVAILABLE_IN_0_20 +TpTLSCertificateState tp_tls_certificate_get_state (TpTLSCertificate *self); + +G_END_DECLS + +#endif /* multiple-inclusion guard */ diff --git a/telepathy-glib/versions/0.19.0.abi b/telepathy-glib/versions/0.19.0.abi new file mode 100644 index 000000000..069e86ec3 --- /dev/null +++ b/telepathy-glib/versions/0.19.0.abi @@ -0,0 +1,97 @@ +Version: TELEPATHY_GLIB_0.19.0 +Extends: TELEPATHY_GLIB_0.18.0 +Release: 0.19.0 + +tp_room_info_get_subject +tp_debug_message_get_category +tp_room_info_get_requires_password +tp_account_channel_request_new_audio_video_call +tp_debug_client_init_known_interfaces +tp_room_info_get_handle +tp_account_channel_request_set_request_property +tp_room_info_get_server +tp_cli_authentication_tls_certificate_call_reject +tp_tls_certificate_get_rejection +tp_capabilities_supports_file_transfer_uri +tp_room_list_start +tp_tls_certificate_add_rejection +tp_svc_connection_interface_contacts_implement_get_contact_by_id +tp_tls_certificate_rejection_get_error +tp_debug_client_get_type +tp_capabilities_supports_file_transfer_initial_offset +tp_room_info_get_description +tp_tls_certificate_rejection_raise_error +tp_cli_debug_connect_to_new_debug_message +tp_tls_certificate_rejection_get_type +tp_capabilities_supports_sms +tp_debug_client_set_enabled_finish +tp_room_info_get_type +tp_connection_manager_param_dup_default_variant +tp_room_info_get_invite_only +tp_tls_certificate_rejection_get_details +tp_connection_dup_contact_by_id_async +tp_tls_certificate_init_known_interfaces +tp_account_channel_request_set_target_id +tp_debug_client_new +tp_tls_certificate_new +tp_connection_upgrade_contacts_finish +tp_tls_certificate_get_feature_quark_core +tp_tls_certificate_get_cert_data +tp_room_info_get_handle_name +tp_tls_certificate_rejection_get_reason +tp_tls_certificate_get_type +tp_room_list_get_account +tp_tls_certificate_reject_finish +tp_cli_connection_interface_contacts_call_get_contact_by_id +tp_capabilities_supports_file_transfer_timestamp +tp_room_info_get_channel_type +tp_tls_certificate_get_nth_rejection +tp_debug_client_set_enabled_async +tp_capabilities_dup_channel_classes_variant +tp_connection_dup_contact_by_id_finish +tp_debug_client_get_feature_quark_core +tp_room_info_get_name +tp_account_channel_request_set_target_contact +tp_connection_dup_detailed_error_vardict +tp_tls_certificate_get_cert_type +tp_cli_authentication_tls_certificate_call_accept +tp_tls_certificate_rejection_get_dbus_error +tp_tls_certificate_accept_async +tp_debug_client_is_enabled +tp_account_channel_request_new_audio_call +tp_debug_message_get_domain +tp_debug_client_get_messages_finish +tp_room_info_get_members_count +tp_account_channel_request_set_file_transfer_timestamp +tp_debug_message_get_type +tp_tls_certificate_get_state +tp_debug_message_get_time +tp_debug_client_get_messages_async +tp_cli_authentication_tls_certificate_connect_to_accepted +tp_contact_get_account +tp_connection_upgrade_contacts_async +tp_account_channel_request_set_file_transfer_initial_offset +tp_debug_message_get_message +tp_tls_certificate_accept_finish +tp_debug_message_get_level +tp_room_list_get_server +tp_account_channel_request_new_text +tp_cli_debug_call_get_messages +tp_account_channel_request_set_file_transfer_description +tp_room_list_get_type +tp_room_list_new_async +tp_room_list_is_listing +tp_room_list_new_finish +tp_room_info_get_room_id +tp_base_connection_disconnect_with_dbus_error_vardict +tp_account_channel_request_new_file_transfer +tp_cli_authentication_tls_certificate_connect_to_rejected +tp_account_channel_request_set_file_transfer_uri +tp_capabilities_supports_file_transfer_description +tp_tls_certificate_reject_async +tp_text_channel_get_feature_quark_chat_states +tp_text_channel_get_chat_state +tp_message_mixin_chat_state_iface_init +tp_message_mixin_maybe_send_gone +tp_message_mixin_change_chat_state +tp_message_mixin_implement_send_chat_state diff --git a/tests/dbus/Makefile.am b/tests/dbus/Makefile.am index c111112a3..53c198656 100644 --- a/tests/dbus/Makefile.am +++ b/tests/dbus/Makefile.am @@ -55,6 +55,7 @@ tests_list = \ test-stream-tube \ test-text-channel \ test-text-respawn \ + test-tls-certificate \ test-unsupported-interface if ENABLE_INSTALLED_TESTS @@ -248,6 +249,8 @@ test_debug_client_SOURCES = debug-client.c test_room_list_SOURCES = room-list.c +test_tls_certificate_SOURCES = tls-certificate.c + check_c_sources = *.c include $(top_srcdir)/tools/check-coding-style.mk check-local: check-coding-style diff --git a/tests/dbus/text-channel.c b/tests/dbus/text-channel.c index 32ccde569..fca77954e 100644 --- a/tests/dbus/text-channel.c +++ b/tests/dbus/text-channel.c @@ -966,6 +966,81 @@ test_receive_muc_delivery (Test *test, g_ptr_array_unref (parts); } +static void +set_chat_state_cb (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + Test *test = user_data; + + tp_text_channel_set_chat_state_finish (TP_TEXT_CHANNEL (source), result, + &test->error); + + test->wait--; + if (test->wait <= 0) + g_main_loop_quit (test->mainloop); +} + +static void +contact_chat_state_changed_cb (TpTextChannel *channel, + TpContact *contact, + TpChannelChatState state, + Test *test) +{ + test->wait--; + if (test->wait <= 0) + g_main_loop_quit (test->mainloop); +} + +static void +test_chat_state (Test *test, + gconstpointer data G_GNUC_UNUSED) +{ + GQuark features[] = { + TP_CHANNEL_FEATURE_CONTACTS, + TP_TEXT_CHANNEL_FEATURE_CHAT_STATES, + 0 }; + TpContact *contact; + TpChannelChatState state; + + /* Set an initial chat state, prepare the channel, and verify target contact + * has that state */ + tp_message_mixin_change_chat_state (G_OBJECT (test->chan_service), + test->bob, TP_CHANNEL_CHAT_STATE_COMPOSING); + + tp_tests_proxy_run_until_prepared (test->channel, features); + + contact = tp_channel_get_target_contact ((TpChannel *) test->channel); + state = tp_text_channel_get_chat_state (test->channel, contact); + g_assert_cmpuint (state, ==, TP_CHANNEL_CHAT_STATE_COMPOSING); + + /* Test setting invalid chat state */ + tp_text_channel_set_chat_state_async (test->channel, -1, + set_chat_state_cb, test); + g_main_loop_run (test->mainloop); + g_assert_error (test->error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT); + g_clear_error (&test->error); + + tp_text_channel_set_chat_state_async (test->channel, + TP_CHANNEL_CHAT_STATE_GONE, set_chat_state_cb, test); + g_main_loop_run (test->mainloop); + g_assert_error (test->error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT); + g_clear_error (&test->error); + + /* Now set a valid chat state and verify self contact has that state */ + tp_text_channel_set_chat_state_async (test->channel, + TP_CHANNEL_CHAT_STATE_COMPOSING, set_chat_state_cb, test); + g_signal_connect (test->channel, "contact-chat-state-changed", + G_CALLBACK (contact_chat_state_changed_cb), test); + test->wait = 2; + g_main_loop_run (test->mainloop); + g_assert_no_error (test->error); + + contact = tp_connection_get_self_contact (test->connection); + state = tp_text_channel_get_chat_state (test->channel, contact); + g_assert_cmpuint (state, ==, TP_CHANNEL_CHAT_STATE_COMPOSING); +} + int main (int argc, char **argv) @@ -1001,6 +1076,8 @@ main (int argc, test_sent_with_no_sender, teardown); g_test_add ("/text-channel/receive-muc-delivery", Test, NULL, setup, test_receive_muc_delivery, teardown); + g_test_add ("/text-channel/chat-state", Test, NULL, setup, + test_chat_state, teardown); return g_test_run (); } diff --git a/tests/dbus/tls-certificate.c b/tests/dbus/tls-certificate.c new file mode 100644 index 000000000..752ea6d30 --- /dev/null +++ b/tests/dbus/tls-certificate.c @@ -0,0 +1,355 @@ +/* Tests of TpTLSCertificate + * + * Copyright © 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 + * notice and this notice are preserved. + */ + +#include "config.h" + +#include <string.h> + +#include <telepathy-glib/telepathy-glib.h> + +#include "tests/lib/contacts-conn.h" +#include "tests/lib/tls-certificate.h" +#include "tests/lib/util.h" + +typedef struct { + GMainLoop *mainloop; + TpDBusDaemon *dbus; + + /* Service side objects */ + TpBaseConnection *base_connection; + TpTestsTLSCertificate *service_cert; + + /* Client side objects */ + TpConnection *connection; + TpTLSCertificate *cert; + + GError *error /* initialized where needed */; + gint wait; +} Test; + + +static void +setup (Test *test, + gconstpointer data) +{ + gchar *path; + GPtrArray *chain_data; + GArray *cert; + + test->mainloop = g_main_loop_new (NULL, FALSE); + test->dbus = tp_tests_dbus_daemon_dup_or_die (); + + test->error = NULL; + + /* Create (service and client sides) connection objects */ + tp_tests_create_and_connect_conn (TP_TESTS_TYPE_CONTACTS_CONNECTION, + "me@test.com", &test->base_connection, &test->connection); + + path = g_strdup_printf ("%s/TlsCertificate", + tp_proxy_get_object_path (test->connection)); + + chain_data = g_ptr_array_new_with_free_func ((GDestroyNotify) g_array_unref); + + cert = g_array_new (TRUE, TRUE, sizeof (guchar)); + g_array_append_vals (cert, "BADGER", 6); + g_ptr_array_add (chain_data, cert); + + test->service_cert = g_object_new (TP_TESTS_TYPE_TLS_CERTIFICATE, + "object-path", path, + "certificate-type", "x509", + "certificate-chain-data", chain_data, + "dbus-daemon", test->dbus, + NULL); + + g_ptr_array_unref (chain_data); + + test->cert = tp_tls_certificate_new (TP_PROXY (test->connection), path, + &test->error); + g_assert_no_error (test->error); + + g_free (path); +} + +static void +disconnect_conn (Test *test) +{ + if (test->connection == NULL) + return; + + tp_tests_connection_assert_disconnect_succeeds (test->connection); + tp_clear_object (&test->connection); + tp_clear_object (&test->base_connection); +} + +static void +teardown (Test *test, + gconstpointer data) +{ + g_clear_error (&test->error); + + tp_clear_object (&test->dbus); + g_main_loop_unref (test->mainloop); + test->mainloop = NULL; + + tp_clear_object (&test->service_cert); + tp_clear_object (&test->cert); + + disconnect_conn (test); +} + +static void +test_creation (Test *test, + gconstpointer data G_GNUC_UNUSED) +{ + g_assert (TP_IS_TLS_CERTIFICATE (test->cert)); +} + +static void +proxy_prepare_cb (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + Test *test = user_data; + + tp_proxy_prepare_finish (source, result, &test->error); + + test->wait--; + if (test->wait <= 0) + g_main_loop_quit (test->mainloop); +} + +static void +prepare_cert (Test *test, + TpTLSCertificate *cert) +{ + GQuark features[] = { TP_TLS_CERTIFICATE_FEATURE_CORE, 0 }; + + tp_proxy_prepare_async (cert, features, proxy_prepare_cb, test); + + test->wait = 1; + g_main_loop_run (test->mainloop); + g_assert_no_error (test->error); +} + +static void +test_core (Test *test, + gconstpointer data G_GNUC_UNUSED) +{ + GPtrArray *cert_data; + GBytes *d; + + /* Properties are not valid yet */ + g_assert_cmpstr (tp_tls_certificate_get_cert_type (test->cert), ==, NULL); + g_assert (tp_tls_certificate_get_cert_data (test->cert) == NULL); + g_assert_cmpuint (tp_tls_certificate_get_state (test->cert), ==, + TP_TLS_CERTIFICATE_STATE_PENDING); + + prepare_cert (test, test->cert); + + g_assert_cmpstr (tp_tls_certificate_get_cert_type (test->cert), ==, "x509"); + g_assert_cmpuint (tp_tls_certificate_get_state (test->cert), ==, + TP_TLS_CERTIFICATE_STATE_PENDING); + + cert_data = tp_tls_certificate_get_cert_data (test->cert); + g_assert (cert_data != NULL); + g_assert_cmpuint (cert_data->len, ==, 1); + d = g_ptr_array_index (cert_data, 0); + g_assert_cmpstr (g_bytes_get_data (d, NULL), ==, "BADGER"); +} + +static void +notify_cb (GObject *object, + GParamSpec *spec, + Test *test) +{ + test->wait--; + if (test->wait <= 0) + g_main_loop_quit (test->mainloop); +} + +static void +accept_cb (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + Test *test = user_data; + + tp_tls_certificate_accept_finish (TP_TLS_CERTIFICATE (source), result, + &test->error); + + test->wait--; + if (test->wait <= 0) + g_main_loop_quit (test->mainloop); +} + +static void +test_accept (Test *test, + gconstpointer data G_GNUC_UNUSED) +{ + g_signal_connect (test->cert, "notify::state", + G_CALLBACK (notify_cb), test); + + tp_tls_certificate_accept_async (test->cert, accept_cb, test); + + test->wait = 2; + g_main_loop_run (test->mainloop); + g_assert_no_error (test->error); + + g_assert_cmpuint (tp_tls_certificate_get_state (test->cert), ==, + TP_TLS_CERTIFICATE_STATE_ACCEPTED); +} + +static void +reject_cb (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + Test *test = user_data; + + tp_tls_certificate_reject_finish (TP_TLS_CERTIFICATE (source), result, + &test->error); + + test->wait--; + if (test->wait <= 0) + g_main_loop_quit (test->mainloop); +} + +static void +test_reject (Test *test, + gconstpointer data G_GNUC_UNUSED) +{ + GVariant *details; + const GError *error; + TpTLSCertificateRejectReason reason; + const gchar *dbus_error; + gboolean enabled; + TpTLSCertificate *cert; + TpTLSCertificateRejection *rej; + GError *err = NULL; + + g_signal_connect (test->cert, "notify::state", + G_CALLBACK (notify_cb), test); + + + tp_tls_certificate_add_rejection (test->cert, + TP_TLS_CERTIFICATE_REJECT_REASON_REVOKED, NULL, + g_variant_new_parsed ("{ 'user-requested': <%b> }", TRUE)); + tp_tls_certificate_add_rejection (test->cert, + TP_TLS_CERTIFICATE_REJECT_REASON_UNKNOWN, + TP_ERROR_STR_CAPTCHA_NOT_SUPPORTED, NULL); + + tp_tls_certificate_reject_async (test->cert, reject_cb, test); + + test->wait = 2; + g_main_loop_run (test->mainloop); + g_assert_no_error (test->error); + + g_assert_cmpuint (tp_tls_certificate_get_state (test->cert), ==, + TP_TLS_CERTIFICATE_STATE_REJECTED); + + rej = tp_tls_certificate_get_rejection (test->cert); + g_assert (TP_IS_TLS_CERTIFICATE_REJECTION (rej)); + error = tp_tls_certificate_rejection_get_error (rej); + dbus_error = tp_tls_certificate_rejection_get_dbus_error (rej); + reason = tp_tls_certificate_rejection_get_reason (rej); + details = tp_tls_certificate_rejection_get_details (rej); + + g_assert_error (error, TP_ERROR, TP_ERROR_CERT_REVOKED); + g_assert_cmpstr (dbus_error, ==, TP_ERROR_STR_CERT_REVOKED); + g_assert_cmpuint (reason, ==, TP_TLS_CERTIFICATE_REJECT_REASON_REVOKED); + g_assert (g_variant_is_of_type (details, G_VARIANT_TYPE_VARDICT)); + g_assert_cmpuint (g_variant_n_children (details), ==, 1); + g_assert (g_variant_lookup (details, "user-requested", "b", &enabled)); + g_assert (enabled); + + g_assert (!tp_tls_certificate_rejection_raise_error (rej, &err)); + g_assert_error (err, TP_ERROR, TP_ERROR_CERT_REVOKED); + g_error_free (err); + + rej = tp_tls_certificate_get_nth_rejection (test->cert, 1); + g_assert (TP_IS_TLS_CERTIFICATE_REJECTION (rej)); + error = tp_tls_certificate_rejection_get_error (rej); + dbus_error = tp_tls_certificate_rejection_get_dbus_error (rej); + details = tp_tls_certificate_rejection_get_details (rej); + + g_assert_error (error, TP_ERROR, TP_ERROR_CAPTCHA_NOT_SUPPORTED); + g_assert_cmpstr (dbus_error, ==, TP_ERROR_STR_CAPTCHA_NOT_SUPPORTED); + g_assert (g_variant_is_of_type (details, G_VARIANT_TYPE_VARDICT)); + g_assert_cmpuint (g_variant_n_children (details), ==, 0); + + /* Test if we cope with an empty rejections list */ + tp_tests_tls_certificate_clear_rejection (test->service_cert); + + cert = tp_tls_certificate_new (TP_PROXY (test->connection), + tp_proxy_get_object_path (test->cert), &test->error); + g_assert_no_error (test->error); + + prepare_cert (test, cert); + + rej = tp_tls_certificate_get_rejection (cert); + g_assert (TP_IS_TLS_CERTIFICATE_REJECTION (rej)); + error = tp_tls_certificate_rejection_get_error (rej); + dbus_error = tp_tls_certificate_rejection_get_dbus_error (rej); + details = tp_tls_certificate_rejection_get_details (rej); + + g_assert_error (error, TP_ERROR, TP_ERROR_CERT_INVALID); + g_assert_cmpstr (dbus_error, ==, TP_ERROR_STR_CERT_INVALID); + g_assert (g_variant_is_of_type (details, G_VARIANT_TYPE_VARDICT)); + g_assert_cmpuint (g_variant_n_children (details), ==, 0); + + g_object_unref (cert); +} + +static void +invalidated_cb (TpProxy *cert, + guint domain, + gint code, + const gchar *message, + Test *test) +{ + g_clear_error (&test->error); + test->error = g_error_new_literal (domain, code, message); + + test->wait--; + if (test->wait <= 0) + g_main_loop_quit (test->mainloop); +} + +static void +test_invalidated (Test *test, + gconstpointer data G_GNUC_UNUSED) +{ + g_signal_connect (test->cert, "invalidated", + G_CALLBACK (invalidated_cb), test); + + disconnect_conn (test); + + g_assert_error (test->error, TP_ERROR, TP_ERROR_CANCELLED); +} + +int +main (int argc, + char **argv) +{ + tp_tests_init (&argc, &argv); + g_test_bug_base ("http://bugs.freedesktop.org/show_bug.cgi?id="); + + g_test_add ("/tls-certificate/creation", Test, NULL, setup, + test_creation, teardown); + g_test_add ("/tls-certificate/core", Test, NULL, setup, + test_core, teardown); + g_test_add ("/tls-certificate/accept", Test, NULL, setup, + test_accept, teardown); + g_test_add ("/tls-certificate/reject", Test, NULL, setup, + test_reject, teardown); + g_test_add ("/tls-certificate/invalidated", Test, NULL, setup, + test_invalidated, teardown); + + return g_test_run (); +} diff --git a/tests/lib/Makefile.am b/tests/lib/Makefile.am index c63d2e332..75b8c005a 100644 --- a/tests/lib/Makefile.am +++ b/tests/lib/Makefile.am @@ -69,6 +69,8 @@ libtp_glib_tests_la_SOURCES = \ textchan-null.h \ textchan-group.c \ textchan-group.h \ + tls-certificate.h \ + tls-certificate.c \ util.c \ util.h libtp_glib_tests_internal_la_SOURCES = $(libtp_glib_tests_la_SOURCES) diff --git a/tests/lib/tls-certificate.c b/tests/lib/tls-certificate.c new file mode 100644 index 000000000..e61b2dd1c --- /dev/null +++ b/tests/lib/tls-certificate.c @@ -0,0 +1,353 @@ +/* + * tls-certificate.c - Source for TpTestsTLSCertificate + * Copyright (C) 2010 Collabora Ltd. + * @author Cosimo Cecchi <cosimo.cecchi@collabora.co.uk> + * + * 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 + */ + +#include "config.h" +#include "tls-certificate.h" + +#include <telepathy-glib/gtypes.h> +#include <telepathy-glib/interfaces.h> +#include <telepathy-glib/telepathy-glib.h> +#include <telepathy-glib/svc-generic.h> +#include <telepathy-glib/svc-tls.h> + +#define DEBUG_FLAG TP_TESTS_DEBUG_TLS +#include "debug.h" + +static void +tls_certificate_iface_init (gpointer g_iface, gpointer iface_data); + +G_DEFINE_TYPE_WITH_CODE (TpTestsTLSCertificate, + tp_tests_tls_certificate, + G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_AUTHENTICATION_TLS_CERTIFICATE, + tls_certificate_iface_init); + G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_DBUS_PROPERTIES, + tp_dbus_properties_mixin_iface_init);) + +struct _TpTestsTLSCertificatePrivate { + gchar *object_path; + + gchar *cert_type; + TpTLSCertificateState cert_state; + + GPtrArray *rejections; + GPtrArray *cert_data; + + TpDBusDaemon *daemon; + + gboolean dispose_has_run; +}; + +enum { + PROP_OBJECT_PATH = 1, + PROP_STATE, + PROP_REJECTIONS, + PROP_CERTIFICATE_TYPE, + PROP_CERTIFICATE_CHAIN_DATA, + + /* not exported */ + PROP_DBUS_DAEMON, + + NUM_PROPERTIES +}; + +static void +tp_tests_tls_certificate_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + TpTestsTLSCertificate *self = TP_TESTS_TLS_CERTIFICATE (object); + + switch (property_id) + { + case PROP_OBJECT_PATH: + g_value_set_string (value, self->priv->object_path); + break; + case PROP_STATE: + g_value_set_uint (value, self->priv->cert_state); + break; + case PROP_REJECTIONS: + g_value_set_boxed (value, self->priv->rejections); + break; + case PROP_CERTIFICATE_TYPE: + g_value_set_string (value, self->priv->cert_type); + break; + case PROP_CERTIFICATE_CHAIN_DATA: + g_value_set_boxed (value, self->priv->cert_data); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +tp_tests_tls_certificate_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + TpTestsTLSCertificate *self = TP_TESTS_TLS_CERTIFICATE (object); + + switch (property_id) + { + case PROP_OBJECT_PATH: + self->priv->object_path = g_value_dup_string (value); + break; + case PROP_CERTIFICATE_TYPE: + self->priv->cert_type = g_value_dup_string (value); + break; + case PROP_CERTIFICATE_CHAIN_DATA: + self->priv->cert_data = g_value_dup_boxed (value); + break; + case PROP_DBUS_DAEMON: + self->priv->daemon = g_value_dup_object (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, value); + break; + } +} + +static void +tp_tests_tls_certificate_finalize (GObject *object) +{ + TpTestsTLSCertificate *self = TP_TESTS_TLS_CERTIFICATE (object); + + tp_clear_boxed (TP_ARRAY_TYPE_TLS_CERTIFICATE_REJECTION_LIST, + &self->priv->rejections); + + g_free (self->priv->object_path); + g_free (self->priv->cert_type); + g_ptr_array_unref (self->priv->cert_data); + + G_OBJECT_CLASS (tp_tests_tls_certificate_parent_class)->finalize (object); +} + +static void +tp_tests_tls_certificate_dispose (GObject *object) +{ + TpTestsTLSCertificate *self = TP_TESTS_TLS_CERTIFICATE (object); + + if (self->priv->dispose_has_run) + return; + + self->priv->dispose_has_run = TRUE; + + tp_clear_object (&self->priv->daemon); + + G_OBJECT_CLASS (tp_tests_tls_certificate_parent_class)->dispose (object); +} + +static void +tp_tests_tls_certificate_constructed (GObject *object) +{ + TpTestsTLSCertificate *self = TP_TESTS_TLS_CERTIFICATE (object); + void (*chain_up) (GObject *) = + G_OBJECT_CLASS (tp_tests_tls_certificate_parent_class)->constructed; + + if (chain_up != NULL) + chain_up (object); + + /* register the certificate on the bus */ + tp_dbus_daemon_register_object (self->priv->daemon, + self->priv->object_path, self); +} + +static void +tp_tests_tls_certificate_init (TpTestsTLSCertificate *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, + TP_TESTS_TYPE_TLS_CERTIFICATE, TpTestsTLSCertificatePrivate); + self->priv->rejections = g_ptr_array_new (); +} + +static void +tp_tests_tls_certificate_class_init (TpTestsTLSCertificateClass *klass) +{ + static TpDBusPropertiesMixinPropImpl object_props[] = { + { "State", "state", NULL }, + { "Rejections", "rejections", NULL }, + { "CertificateType", "certificate-type", NULL }, + { "CertificateChainData", "certificate-chain-data", NULL }, + { NULL } + }; + static TpDBusPropertiesMixinIfaceImpl prop_interfaces[] = { + { TP_IFACE_AUTHENTICATION_TLS_CERTIFICATE, + tp_dbus_properties_mixin_getter_gobject_properties, + NULL, + object_props, + }, + { NULL } + }; + GObjectClass *oclass = G_OBJECT_CLASS (klass); + GParamSpec *pspec; + + g_type_class_add_private (klass, sizeof (TpTestsTLSCertificatePrivate)); + + oclass->finalize = tp_tests_tls_certificate_finalize; + oclass->dispose = tp_tests_tls_certificate_dispose; + oclass->set_property = tp_tests_tls_certificate_set_property; + oclass->get_property = tp_tests_tls_certificate_get_property; + oclass->constructed = tp_tests_tls_certificate_constructed; + + pspec = g_param_spec_string ("object-path", + "D-Bus object path", + "The D-Bus object path used for this object on the bus.", + NULL, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (oclass, PROP_OBJECT_PATH, pspec); + + pspec = g_param_spec_uint ("state", + "State of this certificate", + "The state of this TLS certificate.", + 0, TP_NUM_TLS_CERTIFICATE_STATES - 1, + TP_TLS_CERTIFICATE_STATE_PENDING, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (oclass, PROP_STATE, pspec); + + pspec = g_param_spec_boxed ("rejections", + "The reject reasons", + "The reasons why this TLS certificate has been rejected", + TP_ARRAY_TYPE_TLS_CERTIFICATE_REJECTION_LIST, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (oclass, PROP_REJECTIONS, pspec); + + pspec = g_param_spec_string ("certificate-type", + "The certificate type", + "The type of this certificate.", + NULL, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (oclass, PROP_CERTIFICATE_TYPE, pspec); + + pspec = g_param_spec_boxed ("certificate-chain-data", + "The certificate chain data", + "The raw PEM-encoded trust chain of this certificate.", + TP_ARRAY_TYPE_UCHAR_ARRAY_LIST, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (oclass, PROP_CERTIFICATE_CHAIN_DATA, pspec); + + pspec = g_param_spec_object ("dbus-daemon", + "The DBus daemon connection", + "The connection to the DBus daemon owning the CM", + TP_TYPE_DBUS_DAEMON, + G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (oclass, PROP_DBUS_DAEMON, pspec); + + klass->dbus_props_class.interfaces = prop_interfaces; + tp_dbus_properties_mixin_class_init (oclass, + G_STRUCT_OFFSET (TpTestsTLSCertificateClass, dbus_props_class)); +} + +static void +tp_tests_tls_certificate_accept (TpSvcAuthenticationTLSCertificate *cert, + DBusGMethodInvocation *context) +{ + TpTestsTLSCertificate *self = TP_TESTS_TLS_CERTIFICATE (cert); + + DEBUG ("Accept() called on the TLS certificate; current state %u", + self->priv->cert_state); + + if (self->priv->cert_state != TP_TLS_CERTIFICATE_STATE_PENDING) + { + GError error = + { TP_ERROR, + TP_ERROR_INVALID_ARGUMENT, + "Calling Accept() on a certificate with state != PENDING " + "doesn't make sense." + }; + + dbus_g_method_return_error (context, &error); + return; + } + + self->priv->cert_state = TP_TLS_CERTIFICATE_STATE_ACCEPTED; + tp_svc_authentication_tls_certificate_emit_accepted (self); + + tp_svc_authentication_tls_certificate_return_from_accept (context); +} + +static void +tp_tests_tls_certificate_reject (TpSvcAuthenticationTLSCertificate *cert, + const GPtrArray *rejections, + DBusGMethodInvocation *context) +{ + TpTestsTLSCertificate *self = TP_TESTS_TLS_CERTIFICATE (cert); + + DEBUG ("Reject() called on the TLS certificate with rejections %p, " + "length %u; current state %u", rejections, rejections->len, + self->priv->cert_state); + + if (rejections->len < 1) + { + GError error = { TP_ERROR, TP_ERROR_INVALID_ARGUMENT, + "Calling Reject() with a zero-length rejection list." }; + + dbus_g_method_return_error (context, &error); + return; + } + + if (self->priv->cert_state != TP_TLS_CERTIFICATE_STATE_PENDING) + { + GError error = + { TP_ERROR, + TP_ERROR_INVALID_ARGUMENT, + "Calling Reject() on a certificate with state != PENDING " + "doesn't make sense." + }; + + dbus_g_method_return_error (context, &error); + return; + } + + tp_clear_boxed (TP_ARRAY_TYPE_TLS_CERTIFICATE_REJECTION_LIST, + &self->priv->rejections); + + self->priv->rejections = + g_boxed_copy (TP_ARRAY_TYPE_TLS_CERTIFICATE_REJECTION_LIST, + rejections); + self->priv->cert_state = TP_TLS_CERTIFICATE_STATE_REJECTED; + + tp_svc_authentication_tls_certificate_emit_rejected ( + self, self->priv->rejections); + + tp_svc_authentication_tls_certificate_return_from_reject (context); +} + +static void +tls_certificate_iface_init (gpointer g_iface, + gpointer iface_data) +{ + TpSvcAuthenticationTLSCertificateClass *klass = g_iface; + +#define IMPLEMENT(x) \ + tp_svc_authentication_tls_certificate_implement_##x ( \ + klass, tp_tests_tls_certificate_##x) + IMPLEMENT (accept); + IMPLEMENT (reject); +#undef IMPLEMENT +} + +void +tp_tests_tls_certificate_clear_rejection (TpTestsTLSCertificate *self) +{ + g_ptr_array_set_size (self->priv->rejections, 0); +} diff --git a/tests/lib/tls-certificate.h b/tests/lib/tls-certificate.h new file mode 100644 index 000000000..7ce0cf4aa --- /dev/null +++ b/tests/lib/tls-certificate.h @@ -0,0 +1,68 @@ +/* + * tls-certificate.h - Header for TpTestsTLSCertificate + * Copyright (C) 2010 Collabora Ltd. + * @author Cosimo Cecchi <cosimo.cecchi@collabora.co.uk> + * + * 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_TESTS_TLS_CERTIFICATE_H__ +#define __TP_TESTS_TLS_CERTIFICATE_H__ + +#include <glib-object.h> + +#include <telepathy-glib/dbus-properties-mixin.h> + +G_BEGIN_DECLS + +typedef struct _TpTestsTLSCertificate TpTestsTLSCertificate; +typedef struct _TpTestsTLSCertificateClass TpTestsTLSCertificateClass; +typedef struct _TpTestsTLSCertificatePrivate TpTestsTLSCertificatePrivate; + +struct _TpTestsTLSCertificateClass { + GObjectClass parent_class; + + TpDBusPropertiesMixinClass dbus_props_class; +}; + +struct _TpTestsTLSCertificate { + GObject parent; + + TpTestsTLSCertificatePrivate *priv; +}; + +GType tp_tests_tls_certificate_get_type (void); + +#define TP_TESTS_TYPE_TLS_CERTIFICATE \ + (tp_tests_tls_certificate_get_type ()) +#define TP_TESTS_TLS_CERTIFICATE(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), TP_TESTS_TYPE_TLS_CERTIFICATE, \ + TpTestsTLSCertificate)) +#define TP_TESTS_TLS_CERTIFICATE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), TP_TESTS_TYPE_TLS_CERTIFICATE, \ + TpTestsTLSCertificateClass)) +#define TP_TESTS_IS_TLS_CERTIFICATE(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), TP_TESTS_TYPE_TLS_CERTIFICATE)) +#define TP_TESTS_IS_TLS_CERTIFICATE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), TP_TESTS_TYPE_TLS_CERTIFICATE)) +#define TP_TESTS_TLS_CERTIFICATE_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), TP_TESTS_TYPE_TLS_CERTIFICATE, \ + TpTestsTLSCertificateClass)) + +void tp_tests_tls_certificate_clear_rejection (TpTestsTLSCertificate *self); + +G_END_DECLS + +#endif /* #ifndef __TP_TESTS_TLS_CERTIFICATE_H__*/ diff --git a/tests/lib/util.c b/tests/lib/util.c index 3a0c8768c..976029c8f 100644 --- a/tests/lib/util.c +++ b/tests/lib/util.c @@ -477,24 +477,19 @@ tp_tests_connection_assert_disconnect_succeeds (TpConnection *connection) } static void -one_contact_cb (TpConnection *connection, - guint n_contacts, - TpContact * const *contacts, - const gchar * const *good_ids, - GHashTable *bad_ids, - const GError *error, - gpointer user_data, - GObject *weak_object) +one_contact_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) { + TpConnection *connection = (TpConnection *) object; TpContact **contact_loc = user_data; + GError *error = NULL; - g_assert_no_error (error); - g_assert_cmpuint (g_hash_table_size (bad_ids), ==, 0); - g_assert_cmpuint (n_contacts, ==, 1); - g_assert_cmpstr (good_ids[0], !=, NULL); - g_assert (contacts[0] != NULL); + *contact_loc = tp_connection_dup_contact_by_id_finish (connection, result, + &error); - *contact_loc = g_object_ref (contacts[0]); + g_assert_no_error (error); + g_assert (TP_IS_CONTACT (*contact_loc)); } TpContact * @@ -504,8 +499,8 @@ tp_tests_connection_run_until_contact_by_id (TpConnection *connection, { TpContact *contact = NULL; - tp_connection_get_contacts_by_id (connection, 1, &id, features, - one_contact_cb, &contact, NULL, NULL); + tp_connection_dup_contact_by_id_async (connection, id, features, + one_contact_cb, &contact); while (contact == NULL) g_main_context_iteration (NULL, TRUE); diff --git a/tools/check-c-style.sh b/tools/check-c-style.sh index 4330b1479..55834207a 100644 --- a/tools/check-c-style.sh +++ b/tools/check-c-style.sh @@ -3,13 +3,6 @@ fail=0 ( . "${tools_dir}"/check-misc.sh ) || fail=$? -if grep -n '^ *GError *\*[[:alpha:]_][[:alnum:]_]* *;' "$@" -then - echo "^^^ The above files contain uninitialized GError*s - they should be" - echo " initialized to NULL" - fail=1 -fi - # The first regex finds function calls like foo() (as opposed to foo ()). # It attempts to ignore string constants (may cause false negatives). # The second and third ignore block comments (gtkdoc uses foo() as markup). |