diff options
author | Vivek Dasmohapatra <vivek@collabora.co.uk> | 2011-03-21 18:11:17 +0000 |
---|---|---|
committer | Vivek Dasmohapatra <vivek@collabora.co.uk> | 2011-04-05 14:19:10 +0100 |
commit | cb7e7dce7104f1503e854736f86f539d07507880 (patch) | |
tree | d5c2a69b329f115cff89a43cc24b99705ace7b1a | |
parent | 66c6dcfbc51a541da10e07d306b6ba300fd9fc7e (diff) | |
download | telepathy-mission-control-cb7e7dce7104f1503e854736f86f539d07507880.tar.gz |
Implement the draft CD.Messages interface using the internal-request framework
-rw-r--r-- | src/mcd-dispatcher.c | 254 |
1 files changed, 254 insertions, 0 deletions
diff --git a/src/mcd-dispatcher.c b/src/mcd-dispatcher.c index dcc6d83b..0852405c 100644 --- a/src/mcd-dispatcher.c +++ b/src/mcd-dispatcher.c @@ -86,12 +86,16 @@ static void dispatcher_iface_init (gpointer, gpointer); static void redispatch_iface_init (gpointer, gpointer); +static void messages_iface_init (gpointer, gpointer); + G_DEFINE_TYPE_WITH_CODE (McdDispatcher, mcd_dispatcher, MCD_TYPE_MISSION, G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_DISPATCHER, dispatcher_iface_init); G_IMPLEMENT_INTERFACE (MC_TYPE_SVC_CHANNEL_DISPATCHER_INTERFACE_REDISPATCH, redispatch_iface_init); + G_IMPLEMENT_INTERFACE (MC_TYPE_SVC_CHANNEL_DISPATCHER_INTERFACE_MESSAGES_DRAFT, + messages_iface_init); G_IMPLEMENT_INTERFACE ( TP_TYPE_SVC_CHANNEL_DISPATCHER_INTERFACE_OPERATION_LIST, NULL); @@ -2078,6 +2082,256 @@ _mcd_dispatcher_get_client_registry (McdDispatcher *self) return self->priv->clients; } +/* org.freedesktop.Telepathy.ChannelDispatcher.Messages */ +typedef struct +{ + GPtrArray *payload; + guint flags; + guint tries; + gboolean close_after; + DBusGMethodInvocation *dbus_context; +} MessageContext; + +static MessageContext * +message_context_new (const GPtrArray *payload, + guint flags, + DBusGMethodInvocation *dbus_context) +{ + guint i; + const guint size = payload->len; + MessageContext *context = g_slice_new0 (MessageContext); + GPtrArray *msg_copy = g_ptr_array_sized_new (size); + + g_ptr_array_set_free_func (msg_copy, (GDestroyNotify) g_hash_table_unref); + + for (i = 0; i < size; i++) + { + GHashTable *part = g_ptr_array_index (payload, i); + + g_ptr_array_add (msg_copy, _mcd_deepcopy_asv (part)); + } + + context->payload = msg_copy; + context->flags = flags; + context->dbus_context = dbus_context; + + return context; +} + +static void +message_context_free (gpointer ctx) +{ + MessageContext *context = ctx; + + tp_clear_pointer (&context->payload, g_ptr_array_unref); + + if (context->dbus_context != NULL) + { + GError *error; + + error = g_error_new_literal (TP_ERRORS, TP_ERROR_TERMINATED, + "Channel request failed"); + dbus_g_method_return_error (context->dbus_context, error); + g_error_free (error); + } + + g_slice_free (MessageContext, context); +} + +static void +send_message_submitted (TpChannel *proxy, + const gchar *token, + const GError *error, + gpointer data, + GObject *weak) +{ + MessageContext *message = data; + DBusGMethodInvocation *context = message->dbus_context; + McdChannel *channel = MCD_CHANNEL (weak); + McdRequest *request = _mcd_channel_get_request (channel); + gboolean close_after = message->close_after; + McdAccount *account = mcd_channel_get_account (channel); + const gchar *path = mcd_account_get_object_path (account); + + DEBUG ("SEND_MESSAGE_SUBMITTED (error: %p; %s)", + error, error ? error->message : "-"); + + /* this frees the dbus context, so clear it from our cache afterwards */ + if (error == NULL) + mc_svc_channel_dispatcher_interface_messages_draft_return_from_send_message (context, token); + else + dbus_g_method_return_error (context, error); + + message->dbus_context = NULL; + + _mcd_request_clear_internal_handler (request); + + if (close_after) + _mcd_channel_close (channel); + + _mcd_request_unblock_account (path); +} + +static void +send_message_got_channel (McdRequest *request, + McdChannel *channel, + gpointer data, + gboolean close_after) +{ + MessageContext *message = data; + McdAccount *account = _mcd_request_get_account (request); + const gchar *account_path = mcd_account_get_object_path (account); + + DEBUG ("received internal request/channel"); + + /* successful channel creation */ + if (channel != NULL) + { + message->close_after = close_after; + + DEBUG ("calling send on channel interface"); + tp_cli_channel_interface_messages_call_send_message + (mcd_channel_get_tp_channel (channel), + -1, + message->payload, + message->flags, + send_message_submitted, + message, + NULL, + G_OBJECT (channel)); + } + else /* doom and despair: no channel */ + { + if (message->tries++ == 0) + { + /* FIXME: the intention is to keep the account blocked and * + * retry the transmission here: no tim eto implement this * + * for the first draft but we'll get to it shortly. */ + GError *error = g_error_new_literal (TP_ERRORS, TP_ERROR_CANCELLED, + "Channel closed by user"); + + dbus_g_method_return_error (message->dbus_context, error); + _mcd_request_unblock_account (account_path); + g_error_free (error); + } + else + { + GError *error = g_error_new_literal (TP_ERRORS, TP_ERROR_REJECTED, + "Could not create channel"); + + dbus_g_method_return_error (message->dbus_context, error); + _mcd_request_unblock_account (account_path); + g_error_free (error); + } + } +} + +static void +messages_send_message (McSvcChannelDispatcherInterfaceMessagesDraft *iface, + const gchar *account_path, + const gchar *target_id, + const GPtrArray *payload, + guint flags, + DBusGMethodInvocation *context) +{ + McdAccountManager *am; + McdAccount *account; + McdChannel *channel = NULL; + McdRequest *request = NULL; + GError *error = NULL; + MessageContext *message = NULL; + GHashTable *props = NULL; + GValue c_type = { 0 }; + GValue h_type = { 0 }; + GValue target = { 0 }; + McdDispatcher *self = MCD_DISPATCHER (iface); + + DEBUG ("messages_send_message"); + + g_return_if_fail (account_path != NULL); + + g_object_get (self->priv->master, "account-manager", &am, NULL); + + g_assert (am != NULL); + + account = mcd_account_manager_lookup_account_by_path (am, account_path); + + DEBUG ("%s -> %p", account_path, account); + + if (account == NULL) + { + g_set_error (&error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, + "No such account: %s", account_path); + goto failure; + } + + props = g_hash_table_new_full (g_str_hash, g_str_equal, + NULL, (GDestroyNotify) g_value_unset); + + g_value_init (&c_type, G_TYPE_STRING); + g_value_init (&h_type, G_TYPE_UINT); + g_value_init (&target, G_TYPE_STRING); + + g_value_set_static_string (&c_type, TP_IFACE_CHANNEL_TYPE_TEXT); + g_value_set_uint (&h_type, TP_HANDLE_TYPE_CONTACT); + g_value_set_string (&target, target_id); + + g_hash_table_insert (props, TP_PROP_CHANNEL_CHANNEL_TYPE, &c_type); + g_hash_table_insert (props, TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, &h_type); + g_hash_table_insert (props, TP_PROP_CHANNEL_TARGET_ID, &target); + + DEBUG ("Creating internal request"); + /* compare dispatcher_request_channel: we _are_ the handler for * + * this channel so we don't need to check_preferred_handler here * + * Also: this deep-copies the props hash, so we can throw ours away */ + channel = _mcd_account_create_request (self->priv->clients, + account, props, time (NULL), + NULL, NULL, TRUE, + &request, &error); + g_hash_table_unref (props); + + if (channel == NULL || request == NULL) + { + g_set_error (&error, TP_ERRORS, TP_ERROR_RESOURCE_UNAVAILABLE, + "Could not create channel request"); + goto failure; + } + + DEBUG ("allocating internal handler context"); + message = message_context_new (payload, flags, context); + + DEBUG ("assigning internal handler"); + _mcd_request_set_internal_handler (request, + send_message_got_channel, + message_context_free, + message); + + /* we don't need to predict the handler either, same reason as above * + * we do, however, want to call proceed on the request, as it is ours */ + DEBUG ("calling proceed"); + _mcd_request_proceed (request, NULL); + + goto finished; + +failure: + dbus_g_method_return_error (context, error); + g_error_free (error); + +finished: + /* these are reffed and held open by the request infrastructure */ + tp_clear_object (&channel); + tp_clear_object (&request); +} + +static void +messages_iface_init (gpointer iface, gpointer data G_GNUC_UNUSED) +{ +#define IMPLEMENT(x) \ + mc_svc_channel_dispatcher_interface_messages_draft_implement_##x (iface, messages_##x) + IMPLEMENT (send_message); +#undef IMPLEMENT +} + typedef struct { McdDispatcher *self; |