X-Git-Url: https://git.0d.be/?p=empathy.git;a=blobdiff_plain;f=libempathy%2Fempathy-tp-tube.c;h=7d22f6b783cae6d7d16faedb99e8a8246f4b0f9d;hp=4bd08a7112e952b661a822868e2c2528fa6a9821;hb=f02d96379cb7043611d093921755d699e48689bb;hpb=c600f2479e3f7e4aa088daf92bd81aafae0e828f diff --git a/libempathy/empathy-tp-tube.c b/libempathy/empathy-tp-tube.c index 4bd08a71..7d22f6b7 100644 --- a/libempathy/empathy-tp-tube.c +++ b/libempathy/empathy-tp-tube.c @@ -22,9 +22,10 @@ #include #include +#include #include +#include -#include "empathy-contact-factory.h" #include "empathy-enum-types.h" #include "empathy-tp-tube.h" #include "empathy-utils.h" @@ -32,32 +33,55 @@ #define DEBUG_FLAG EMPATHY_DEBUG_TP #include "empathy-debug.h" +typedef struct { + TpSocketAddressType type; + EmpatyTpTubeAcceptStreamTubeCb *callback; + gpointer user_data; +} EmpathyTpTubeAcceptData; + +static EmpathyTpTubeAcceptData * +new_empathy_tp_tube_accept_data (TpSocketAddressType type, + EmpatyTpTubeAcceptStreamTubeCb *callback, gpointer user_data) +{ + EmpathyTpTubeAcceptData *r; + + r = g_slice_new0 (EmpathyTpTubeAcceptData); + r->type = type; + r->callback = callback; + r->user_data = user_data; + + return r; +} + +static void +free_empathy_tp_tube_accept_data (gpointer data) +{ + g_slice_free (EmpathyTpTubeAcceptData, data); +} + + +typedef struct { + EmpathyTpTubeReadyCb *callback; + gpointer user_data; + GDestroyNotify destroy; + GObject *weak_object; +} ReadyCbData; + + #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyTpTube) typedef struct { TpChannel *channel; - guint id; - guint initiator; - guint type; - gchar *service; - GHashTable *parameters; - guint state; - EmpathyContact *initiator_contact; - EmpathyContactFactory *factory; + EmpTubeChannelState state; + gboolean ready; + GSList *ready_callbacks; } EmpathyTpTubePriv; enum { PROP_0, PROP_CHANNEL, - PROP_TP_TUBES, - PROP_ID, - PROP_INITIATOR, - PROP_TYPE, - PROP_SERVICE, - PROP_PARAMETERS, PROP_STATE, - PROP_INITIATOR_CONTACT }; enum @@ -71,16 +95,16 @@ static guint signals[LAST_SIGNAL]; G_DEFINE_TYPE (EmpathyTpTube, empathy_tp_tube, G_TYPE_OBJECT) static void -tp_tube_state_changed_cb (TpChannel *channel, - guint id, - guint state, +tp_tube_state_changed_cb (TpProxy *proxy, + EmpTubeChannelState state, gpointer user_data, GObject *tube) { EmpathyTpTubePriv *priv = GET_PRIV (tube); - if (id != priv->id) - return; + if (!priv->ready) + /* We didn't get the state yet */ + return; DEBUG ("Tube state changed"); @@ -99,21 +123,6 @@ tp_tube_invalidated_cb (TpChannel *channel, g_signal_emit (tube, signals[DESTROY], 0); } -static void -tp_tube_closed_cb (TpChannel *channel, - guint id, - gpointer user_data, - GObject *tube) -{ - EmpathyTpTubePriv *priv = GET_PRIV (tube); - - if (id != priv->id) - return; - - DEBUG ("Tube closed"); - g_signal_emit (tube, signals[DESTROY], 0); -} - static void tp_tube_async_cb (TpChannel *channel, const GError *error, @@ -137,9 +146,6 @@ tp_tube_set_property (GObject *object, case PROP_CHANNEL: priv->channel = g_value_dup_object (value); break; - case PROP_ID: - priv->id = g_value_get_uint (value); - break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -159,33 +165,118 @@ tp_tube_get_property (GObject *object, case PROP_CHANNEL: g_value_set_object (value, priv->channel); break; - case PROP_ID: - g_value_set_uint (value, priv->id); - break; - case PROP_INITIATOR: - g_value_set_uint (value, priv->initiator); - break; - case PROP_TYPE: - g_value_set_uint (value, priv->type); - break; - case PROP_SERVICE: - g_value_set_string (value, priv->service); - break; - case PROP_PARAMETERS: - g_value_set_boxed (value, priv->parameters); - break; case PROP_STATE: g_value_set_uint (value, priv->state); break; - case PROP_INITIATOR_CONTACT: - g_value_set_object (value, priv->initiator_contact); - break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } +static void weak_object_notify (gpointer data, + GObject *old_object); + +static ReadyCbData * +ready_cb_data_new (EmpathyTpTube *self, + EmpathyTpTubeReadyCb *callback, + gpointer user_data, + GDestroyNotify destroy, + GObject *weak_object) +{ + ReadyCbData *d = g_slice_new0 (ReadyCbData); + d->callback = callback; + d->user_data = user_data; + d->destroy = destroy; + d->weak_object = weak_object; + + if (weak_object != NULL) + g_object_weak_ref (weak_object, weak_object_notify, self); + + return d; +} + +static void +ready_cb_data_free (ReadyCbData *data, + EmpathyTpTube *self) +{ + if (data->destroy != NULL) + data->destroy (data->user_data); + + if (data->weak_object != NULL) + g_object_weak_unref (data->weak_object, + weak_object_notify, self); + + g_slice_free (ReadyCbData, data); +} + +static void +weak_object_notify (gpointer data, + GObject *old_object) +{ + EmpathyTpTube *self = EMPATHY_TP_TUBE (data); + EmpathyTpTubePriv *priv = GET_PRIV (self); + GSList *l, *ln; + + for (l = priv->ready_callbacks ; l != NULL ; l = ln ) + { + ReadyCbData *d = (ReadyCbData *) l->data; + ln = g_slist_next (l); + + if (d->weak_object == old_object) + { + ready_cb_data_free (d, self); + priv->ready_callbacks = g_slist_delete_link (priv->ready_callbacks, + l); + } + } +} + + +static void +tube_is_ready (EmpathyTpTube *self, + const GError *error) +{ + EmpathyTpTubePriv *priv = GET_PRIV (self); + GSList *l; + + priv->ready = TRUE; + + for (l = priv->ready_callbacks ; l != NULL ; l = g_slist_next (l)) + { + ReadyCbData *data = (ReadyCbData *) l->data; + + data->callback (self, error, data->user_data, data->weak_object); + ready_cb_data_free (data, self); + } + + g_slist_free (priv->ready_callbacks); + priv->ready_callbacks = NULL; +} + +static void +got_tube_state_cb (TpProxy *proxy, + const GValue *out_value, + const GError *error, + gpointer user_data, + GObject *weak_object) +{ + EmpathyTpTube *self = EMPATHY_TP_TUBE (user_data); + EmpathyTpTubePriv *priv = GET_PRIV (self); + + if (error != NULL) + { + DEBUG ("Error getting State property: %s", error->message); + } + else + { + priv->state = g_value_get_uint (out_value); + g_object_notify (G_OBJECT (self), "state"); + } + + tube_is_ready (self, error); +} + static GObject * tp_tube_constructor (GType type, guint n_props, @@ -193,9 +284,6 @@ tp_tube_constructor (GType type, { GObject *self; EmpathyTpTubePriv *priv; - GPtrArray *tubes; - guint i; - GError *error = NULL; self = G_OBJECT_CLASS (empathy_tp_tube_parent_class)->constructor ( type, n_props, props); @@ -203,56 +291,16 @@ tp_tube_constructor (GType type, g_signal_connect (priv->channel, "invalidated", G_CALLBACK (tp_tube_invalidated_cb), self); - tp_cli_channel_type_tubes_connect_to_tube_closed (priv->channel, - tp_tube_closed_cb, NULL, NULL, self, NULL); - tp_cli_channel_type_tubes_connect_to_tube_state_changed (priv->channel, - tp_tube_state_changed_cb, NULL, NULL, self, NULL); - - /* FIXME: It is absolutely not opimized to list all tubes to get information - * about our tube, but we don't really have the choice to avoid races. */ - if (!tp_cli_channel_type_tubes_run_list_tubes (priv->channel, -1, &tubes, - &error, NULL)) - { - DEBUG ("Couldn't list tubes: %s", error->message); - g_clear_error (&error); - return self; - } - - for (i = 0; i < tubes->len; i++) - { - GValueArray *values; - guint id; - values = g_ptr_array_index (tubes, i); - id = g_value_get_uint (g_value_array_get_nth (values, 0)); + priv->ready = FALSE; - if (id == priv->id) - { - TpConnection *connection; - MissionControl *mc; - McAccount *account; - - g_object_get (priv->channel, "connection", &connection, NULL); - mc = empathy_mission_control_new (); - account = mission_control_get_account_for_tpconnection (mc, - connection, NULL); - - priv->initiator = g_value_get_uint (g_value_array_get_nth (values, 1)); - priv->type = g_value_get_uint (g_value_array_get_nth (values, 2)); - priv->service = g_value_dup_string (g_value_array_get_nth (values, 3)); - priv->parameters = g_value_dup_boxed (g_value_array_get_nth (values, 4)); - priv->state = g_value_get_uint (g_value_array_get_nth (values, 5)); - priv->initiator_contact = empathy_contact_factory_get_from_handle ( - priv->factory, account, priv->initiator); - - g_object_unref (connection); - g_object_unref (mc); - g_object_unref (account); - } + emp_cli_channel_interface_tube_connect_to_tube_channel_state_changed ( + TP_PROXY (priv->channel), tp_tube_state_changed_cb, NULL, NULL, + self, NULL); - g_value_array_free (values); - } - g_ptr_array_free (tubes, TRUE); + tp_cli_dbus_properties_call_get (priv->channel, -1, + EMP_IFACE_CHANNEL_INTERFACE_TUBE, "State", got_tube_state_cb, + self, NULL, G_OBJECT (self)); return self; } @@ -260,7 +308,9 @@ tp_tube_constructor (GType type, static void tp_tube_finalize (GObject *object) { + EmpathyTpTube *self = EMPATHY_TP_TUBE (object); EmpathyTpTubePriv *priv = GET_PRIV (object); + GSList *l; DEBUG ("Finalizing: %p", object); @@ -268,17 +318,20 @@ tp_tube_finalize (GObject *object) { g_signal_handlers_disconnect_by_func (priv->channel, tp_tube_invalidated_cb, object); - tp_cli_channel_type_tubes_call_close_tube (priv->channel, -1, priv->id, - tp_tube_async_cb, "closing tube", NULL, NULL); + tp_cli_channel_call_close (priv->channel, -1, tp_tube_async_cb, + "closing tube", NULL, NULL); g_object_unref (priv->channel); } - if (priv->initiator_contact) - g_object_unref (priv->initiator_contact); - if (priv->factory) - g_object_unref (priv->factory); - g_free (priv->service); - g_hash_table_destroy (priv->parameters); + for (l = priv->ready_callbacks; l != NULL; l = g_slist_next (l)) + { + ReadyCbData *d = (ReadyCbData *) l->data; + + ready_cb_data_free (d, self); + } + + g_slist_free (priv->ready_callbacks); + priv->ready_callbacks = NULL; G_OBJECT_CLASS (empathy_tp_tube_parent_class)->finalize (object); } @@ -295,43 +348,12 @@ empathy_tp_tube_class_init (EmpathyTpTubeClass *klass) g_object_class_install_property (object_class, PROP_CHANNEL, g_param_spec_object ("channel", "channel", "channel", TP_TYPE_CHANNEL, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_NAME | - G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); - - g_object_class_install_property (object_class, PROP_ID, - g_param_spec_uint ("id", "id", "id", 0, G_MAXUINT, 0, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_NAME | - G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); - - g_object_class_install_property (object_class, PROP_INITIATOR, - g_param_spec_uint ("initiator", "initiator", "initiator", - 0, G_MAXUINT, 0, G_PARAM_READABLE | G_PARAM_STATIC_NAME | - G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); - - g_object_class_install_property (object_class, PROP_TYPE, - g_param_spec_uint ("type", "type", "type", 0, G_MAXUINT, 0, - G_PARAM_READABLE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | - G_PARAM_STATIC_BLURB)); - - g_object_class_install_property (object_class, PROP_SERVICE, - g_param_spec_string ("service", "service", "service", NULL, - G_PARAM_READABLE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | - G_PARAM_STATIC_BLURB)); - - g_object_class_install_property (object_class, PROP_PARAMETERS, - g_param_spec_boxed ("parameters", "parameters", "parameters", - G_TYPE_HASH_TABLE, G_PARAM_READABLE | G_PARAM_STATIC_NAME | - G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_STATE, - g_param_spec_uint ("state", "state", "state", 0, G_MAXUINT, 0, - G_PARAM_READABLE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | - G_PARAM_STATIC_BLURB)); - - g_object_class_install_property (object_class, PROP_INITIATOR_CONTACT, - g_param_spec_object ("initiator-contact", "initiator contact", - "initiator contact", EMPATHY_TYPE_CONTACT, G_PARAM_READABLE | - G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); + g_param_spec_uint ("state", "state", "state", + 0, NUM_EMP_TUBE_CHANNEL_STATES, 0, + G_PARAM_READABLE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_STRINGS)); signals[DESTROY] = g_signal_new ("destroy", G_TYPE_FROM_CLASS (klass), @@ -350,17 +372,14 @@ empathy_tp_tube_init (EmpathyTpTube *tube) EMPATHY_TYPE_TP_TUBE, EmpathyTpTubePriv); tube->priv = priv; - - priv->factory = empathy_contact_factory_new (); } EmpathyTpTube * -empathy_tp_tube_new (TpChannel *channel, guint tube_id) +empathy_tp_tube_new (TpChannel *channel) { g_return_val_if_fail (TP_IS_CHANNEL (channel), NULL); - return g_object_new (EMPATHY_TYPE_TP_TUBE, - "channel", channel, "id", tube_id, NULL); + return g_object_new (EMPATHY_TYPE_TP_TUBE, "channel", channel, NULL); } EmpathyTpTube * @@ -368,32 +387,53 @@ empathy_tp_tube_new_stream_tube (EmpathyContact *contact, TpSocketAddressType type, const gchar *hostname, guint port, - const gchar *service) + const gchar *service, + GHashTable *parameters) { - MissionControl *mc; - McAccount *account; TpConnection *connection; TpChannel *channel; gchar *object_path; - guint id; GHashTable *params; GValue *address; GValue *control_param; EmpathyTpTube *tube = NULL; GError *error = NULL; + GHashTable *request; + GHashTable *channel_properties; + GValue *value; g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL); g_return_val_if_fail (hostname != NULL, NULL); g_return_val_if_fail (service != NULL, NULL); - mc = empathy_mission_control_new (); - account = empathy_contact_get_account (contact); - connection = mission_control_get_tpconnection (mc, account, NULL); - g_object_unref (mc); + connection = empathy_contact_get_connection (contact); + + request = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, + (GDestroyNotify) tp_g_value_slice_free); + + /* org.freedesktop.Telepathy.Channel.ChannelType */ + value = tp_g_value_slice_new (G_TYPE_STRING); + g_value_set_string (value, EMP_IFACE_CHANNEL_TYPE_STREAM_TUBE); + g_hash_table_insert (request, TP_IFACE_CHANNEL ".ChannelType", value); + + /* org.freedesktop.Telepathy.Channel.TargetHandleType */ + value = tp_g_value_slice_new (G_TYPE_UINT); + g_value_set_uint (value, TP_HANDLE_TYPE_CONTACT); + g_hash_table_insert (request, TP_IFACE_CHANNEL ".TargetHandleType", value); - if (!tp_cli_connection_run_request_channel (connection, -1, - TP_IFACE_CHANNEL_TYPE_TUBES, TP_HANDLE_TYPE_CONTACT, - empathy_contact_get_handle (contact), FALSE, &object_path, &error, NULL)) + /* org.freedesktop.Telepathy.Channel.TargetHandleType */ + value = tp_g_value_slice_new (G_TYPE_UINT); + g_value_set_uint (value, empathy_contact_get_handle (contact)); + g_hash_table_insert (request, TP_IFACE_CHANNEL ".TargetHandle", value); + + /* org.freedesktop.Telepathy.Channel.Type.StreamTube.Service */ + value = tp_g_value_slice_new (G_TYPE_STRING); + g_value_set_string (value, service); + g_hash_table_insert (request, + EMP_IFACE_CHANNEL_TYPE_STREAM_TUBE ".Service", value); + + if (!tp_cli_connection_interface_requests_run_create_channel (connection, -1, + request, &object_path, &channel_properties, &error, NULL)) { DEBUG ("Error requesting channel: %s", error->message); g_clear_error (&error); @@ -403,9 +443,10 @@ empathy_tp_tube_new_stream_tube (EmpathyContact *contact, DEBUG ("Offering a new stream tube"); - channel = tp_channel_new (connection, object_path, - TP_IFACE_CHANNEL_TYPE_TUBES, TP_HANDLE_TYPE_CONTACT, - empathy_contact_get_handle (contact), NULL); + channel = tp_channel_new_from_properties (connection, object_path, + channel_properties, NULL); + + tp_channel_run_until_ready (channel, NULL, NULL); #define ADDRESS_TYPE dbus_g_type_get_struct ("GValueArray",\ G_TYPE_STRING, G_TYPE_UINT, G_TYPE_INVALID) @@ -415,94 +456,122 @@ empathy_tp_tube_new_stream_tube (EmpathyContact *contact, dbus_g_type_struct_set (address, 0, hostname, 1, port, G_MAXUINT); control_param = tp_g_value_slice_new (G_TYPE_STRING); - if (!tp_cli_channel_type_tubes_run_offer_stream_tube (channel, -1, - service, params, type, address, - TP_SOCKET_ACCESS_CONTROL_LOCALHOST, control_param, &id, &error, NULL)) + if (parameters == NULL) + /* Pass an empty dict as parameters */ + parameters = g_hash_table_new (g_str_hash, g_str_equal); + else + g_hash_table_ref (parameters); + + if (!emp_cli_channel_type_stream_tube_run_offer_stream_tube ( + TP_PROXY(channel), -1, type, address, + TP_SOCKET_ACCESS_CONTROL_LOCALHOST, control_param, parameters, + &error, NULL)) { DEBUG ("Couldn't offer tube: %s", error->message); g_clear_error (&error); goto OUT; } - DEBUG ("Stream tube id=%d offered", id); + DEBUG ("Stream tube offered"); - tube = empathy_tp_tube_new (channel, id); + tube = empathy_tp_tube_new (channel); OUT: g_object_unref (channel); g_free (object_path); - g_hash_table_destroy (params); + g_hash_table_destroy (request); + g_hash_table_destroy (channel_properties); tp_g_value_slice_free (address); tp_g_value_slice_free (control_param); g_object_unref (connection); + g_hash_table_unref (parameters); return tube; } static void -tp_tube_accept_stream_cb (TpChannel *proxy, +tp_tube_accept_stream_cb (TpProxy *proxy, const GValue *address, const GError *error, gpointer user_data, GObject *weak_object) { + EmpathyTpTube *tube = EMPATHY_TP_TUBE (weak_object); + EmpathyTpTubeAcceptData *data = (EmpathyTpTubeAcceptData *)user_data; + EmpathyTpTubeAddress eaddress; + + eaddress.type = data->type; + if (error) + { DEBUG ("Error accepting tube: %s", error->message); + data->callback (tube, NULL, error, data->user_data); + return; + } + + switch (eaddress.type) + { + case TP_SOCKET_ADDRESS_TYPE_UNIX: + case TP_SOCKET_ADDRESS_TYPE_ABSTRACT_UNIX: + eaddress.a.socket.path = g_value_get_boxed (address); + break; + case TP_SOCKET_ADDRESS_TYPE_IPV4: + case TP_SOCKET_ADDRESS_TYPE_IPV6: + dbus_g_type_struct_get (address, + 0, &eaddress.a.inet.hostname, + 1, &eaddress.a.inet.port, G_MAXUINT); + break; + } + + data->callback (tube, &eaddress, NULL, data->user_data); } void empathy_tp_tube_accept_stream_tube (EmpathyTpTube *tube, - TpSocketAddressType type) + TpSocketAddressType type, EmpatyTpTubeAcceptStreamTubeCb *callback, + gpointer user_data) { EmpathyTpTubePriv *priv = GET_PRIV (tube); GValue *control_param; + EmpathyTpTubeAcceptData *data; g_return_if_fail (EMPATHY_IS_TP_TUBE (tube)); - DEBUG ("Accepting stream tube - id: %d", priv->id); - + DEBUG ("Accepting stream tube"); + /* FIXME allow other acls */ control_param = tp_g_value_slice_new (G_TYPE_STRING); - tp_cli_channel_type_tubes_call_accept_stream_tube (priv->channel, -1, priv->id, - type, TP_SOCKET_ACCESS_CONTROL_LOCALHOST, control_param, - tp_tube_accept_stream_cb, NULL, NULL, G_OBJECT (tube)); + + data = new_empathy_tp_tube_accept_data (type, callback, user_data); + + emp_cli_channel_type_stream_tube_call_accept_stream_tube ( + TP_PROXY (priv->channel), -1, type, TP_SOCKET_ACCESS_CONTROL_LOCALHOST, + control_param, tp_tube_accept_stream_cb, data, + free_empathy_tp_tube_accept_data, G_OBJECT (tube)); tp_g_value_slice_free (control_param); } void -empathy_tp_tube_get_socket (EmpathyTpTube *tube, - gchar **hostname, - guint *port) +empathy_tp_tube_call_when_ready (EmpathyTpTube *self, + EmpathyTpTubeReadyCb *callback, + gpointer user_data, + GDestroyNotify destroy, + GObject *weak_object) { - EmpathyTpTubePriv *priv = GET_PRIV (tube); - GValue *address; - guint address_type; - GError *error = NULL; - - g_return_if_fail (EMPATHY_IS_TP_TUBE (tube)); + EmpathyTpTubePriv *priv = GET_PRIV (self); - DEBUG ("Getting stream tube socket address"); + g_return_if_fail (self != NULL); + g_return_if_fail (callback != NULL); - if (!tp_cli_channel_type_tubes_run_get_stream_tube_socket_address (priv->channel, - -1, priv->id, &address_type, &address, &error, NULL)) + if (priv->ready) { - DEBUG ("Couldn't get socket address: %s", error->message); - g_clear_error (&error); - return; + callback (self, NULL, user_data, weak_object); + if (destroy != NULL) + destroy (user_data); } - - switch (address_type) + else { - case TP_SOCKET_ADDRESS_TYPE_UNIX: - case TP_SOCKET_ADDRESS_TYPE_ABSTRACT_UNIX: - dbus_g_type_struct_get (address, 0, hostname, G_MAXUINT); - break; - case TP_SOCKET_ADDRESS_TYPE_IPV4: - case TP_SOCKET_ADDRESS_TYPE_IPV6: - dbus_g_type_struct_get (address, 0, hostname, 1, port, G_MAXUINT); - break; + priv->ready_callbacks = g_slist_prepend (priv->ready_callbacks, + ready_cb_data_new (self, callback, user_data, destroy, weak_object)); } - - g_free (address); } -