2 * empathy-dispatch-operation.c - Source for EmpathyDispatchOperation
3 * Copyright (C) 2008 Collabora Ltd.
4 * @author Sjoerd Simons <sjoerd.simons@collabora.co.uk>
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
25 #include "empathy-dispatch-operation.h"
26 #include <libempathy/empathy-enum-types.h>
27 #include <libempathy/empathy-tp-contact-factory.h>
28 #include <libempathy/empathy-tp-chat.h>
29 #include <libempathy/empathy-tp-call.h>
30 #include <libempathy/empathy-tp-file.h>
32 #include "empathy-marshal.h"
34 #include "extensions/extensions.h"
36 #define DEBUG_FLAG EMPATHY_DEBUG_DISPATCHER
37 #include <libempathy/empathy-debug.h>
39 G_DEFINE_TYPE(EmpathyDispatchOperation, empathy_dispatch_operation,
42 static void empathy_dispatch_operation_set_status (
43 EmpathyDispatchOperation *self, EmpathyDispatchOperationState status);
44 static void empathy_dispatch_operation_channel_ready_cb (TpChannel *channel,
45 const GError *error, gpointer user_data);
50 /* Ready for dispatching */
52 /* Approved by an approver, can only happens on incoming operations */
54 /* Claimed by a handler */
56 /* Error, channel went away, inspecting it failed etc */
61 static guint signals[LAST_SIGNAL] = {0};
73 /* private structure */
74 typedef struct _EmpathyDispatchOperationPriv \
75 EmpathyDispatchOperationPriv;
77 struct _EmpathyDispatchOperationPriv
79 gboolean dispose_has_run;
80 TpConnection *connection;
82 GObject *channel_wrapper;
83 EmpathyContact *contact;
84 EmpathyDispatchOperationState status;
87 gulong invalidated_handler;
92 (G_TYPE_INSTANCE_GET_PRIVATE ((o), EMPATHY_TYPE_DISPATCH_OPERATION, \
93 EmpathyDispatchOperationPriv))
96 empathy_dispatch_operation_init (EmpathyDispatchOperation *obj)
98 //EmpathyDispatchOperationPriv *priv =
101 /* allocate any data required by the object here */
104 static void empathy_dispatch_operation_dispose (GObject *object);
105 static void empathy_dispatch_operation_finalize (GObject *object);
108 empathy_dispatch_operation_set_property (GObject *object,
109 guint property_id, const GValue *value, GParamSpec *pspec)
111 EmpathyDispatchOperation *operation = EMPATHY_DISPATCH_OPERATION (object);
112 EmpathyDispatchOperationPriv *priv = GET_PRIV (operation);
116 case PROP_CONNECTION:
117 priv->connection = g_value_dup_object (value);
120 priv->channel = g_value_dup_object (value);
122 case PROP_CHANNEL_WRAPPER:
123 priv->channel_wrapper = g_value_dup_object (value);
126 if (priv->contact != NULL)
127 g_object_unref (priv->contact);
128 priv->contact = g_value_dup_object (value);
131 priv->incoming = g_value_get_boolean (value);
137 empathy_dispatch_operation_get_property (GObject *object,
138 guint property_id, GValue *value, GParamSpec *pspec)
140 EmpathyDispatchOperation *operation = EMPATHY_DISPATCH_OPERATION (object);
141 EmpathyDispatchOperationPriv *priv = GET_PRIV (operation);
145 case PROP_CONNECTION:
146 g_value_set_object (value, priv->connection);
149 g_value_set_object (value, priv->channel);
151 case PROP_CHANNEL_WRAPPER:
152 g_value_set_object (value, priv->channel_wrapper);
155 g_value_set_object (value, priv->contact);
158 g_value_set_boolean (value, priv->incoming);
161 g_value_set_enum (value, priv->status);
167 empathy_dispatch_operation_invalidated (TpProxy *proxy, guint domain,
168 gint code, char *message, EmpathyDispatchOperation *self)
170 empathy_dispatch_operation_set_status (self,
171 EMPATHY_DISPATCHER_OPERATION_STATE_INVALIDATED);
173 g_signal_emit (self, signals[INVALIDATED], 0, domain, code, message);
177 dispatcher_operation_got_contact_cb (EmpathyTpContactFactory *factory,
178 EmpathyContact *contact,
183 EmpathyDispatchOperationPriv *priv = GET_PRIV (self);
187 /* FIXME: We should cancel the operation */
188 DEBUG ("Error: %s", error->message);
192 if (priv->contact != NULL)
193 g_object_unref (priv->contact);
194 priv->contact = g_object_ref (contact);
195 g_object_notify (G_OBJECT (self), "contact");
197 tp_channel_call_when_ready (priv->channel,
198 empathy_dispatch_operation_channel_ready_cb, self);
202 empathy_dispatch_operation_constructed (GObject *object)
204 EmpathyDispatchOperation *self = EMPATHY_DISPATCH_OPERATION (object);
205 EmpathyDispatchOperationPriv *priv = GET_PRIV (self);
207 TpHandleType handle_type;
209 empathy_dispatch_operation_set_status (self,
210 EMPATHY_DISPATCHER_OPERATION_STATE_PREPARING);
212 priv->invalidated_handler =
213 g_signal_connect (priv->channel, "invalidated",
214 G_CALLBACK (empathy_dispatch_operation_invalidated), self);
216 handle = tp_channel_get_handle (priv->channel, &handle_type);
218 if (handle_type == TP_CONN_HANDLE_TYPE_CONTACT && priv->contact == NULL)
220 EmpathyTpContactFactory *factory;
222 factory = empathy_tp_contact_factory_dup_singleton (priv->connection);
223 empathy_tp_contact_factory_get_from_handle (factory, handle,
224 dispatcher_operation_got_contact_cb, NULL, NULL, object);
225 g_object_unref (factory);
229 tp_channel_call_when_ready (priv->channel,
230 empathy_dispatch_operation_channel_ready_cb, self);
234 empathy_dispatch_operation_class_init (
235 EmpathyDispatchOperationClass *empathy_dispatch_operation_class)
237 GObjectClass *object_class =
238 G_OBJECT_CLASS (empathy_dispatch_operation_class);
239 GParamSpec *param_spec;
241 g_type_class_add_private (empathy_dispatch_operation_class,
242 sizeof (EmpathyDispatchOperationPriv));
244 object_class->set_property = empathy_dispatch_operation_set_property;
245 object_class->get_property = empathy_dispatch_operation_get_property;
247 object_class->dispose = empathy_dispatch_operation_dispose;
248 object_class->finalize = empathy_dispatch_operation_finalize;
249 object_class->constructed = empathy_dispatch_operation_constructed;
251 signals[READY] = g_signal_new ("ready",
252 G_OBJECT_CLASS_TYPE(empathy_dispatch_operation_class),
256 g_cclosure_marshal_VOID__VOID,
259 signals[APPROVED] = g_signal_new ("approved",
260 G_OBJECT_CLASS_TYPE(empathy_dispatch_operation_class),
264 g_cclosure_marshal_VOID__VOID,
267 signals[CLAIMED] = g_signal_new ("claimed",
268 G_OBJECT_CLASS_TYPE(empathy_dispatch_operation_class),
272 g_cclosure_marshal_VOID__VOID,
275 signals[INVALIDATED] = g_signal_new ("invalidated",
276 G_OBJECT_CLASS_TYPE(empathy_dispatch_operation_class),
280 _empathy_marshal_VOID__UINT_INT_STRING,
281 G_TYPE_NONE, 3, G_TYPE_UINT, G_TYPE_INT, G_TYPE_STRING);
283 param_spec = g_param_spec_object ("connection",
284 "connection", "The telepathy connection",
286 G_PARAM_CONSTRUCT_ONLY |
287 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
288 g_object_class_install_property (object_class, PROP_CONNECTION,
291 param_spec = g_param_spec_object ("channel",
292 "channel", "The telepathy channel",
294 G_PARAM_CONSTRUCT_ONLY |
295 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
296 g_object_class_install_property (object_class, PROP_CHANNEL,
299 param_spec = g_param_spec_object ("channel-wrapper",
300 "channel wrapper", "The empathy specific channel wrapper",
302 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
303 g_object_class_install_property (object_class, PROP_CHANNEL_WRAPPER,
306 param_spec = g_param_spec_object ("contact",
307 "contact", "The empathy contact",
308 EMPATHY_TYPE_CONTACT,
309 G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
310 g_object_class_install_property (object_class, PROP_CONTACT,
313 param_spec = g_param_spec_boolean ("incoming",
314 "incoming", "Whether or not the channel is incoming",
316 G_PARAM_CONSTRUCT_ONLY |
317 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
318 g_object_class_install_property (object_class, PROP_INCOMING,
321 param_spec = g_param_spec_enum ("status",
322 "status", "Status of the dispatch operation",
323 EMPATHY_TYPE_DISPATCH_OPERATION_STATE, 0,
324 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
325 g_object_class_install_property (object_class, PROP_STATUS, param_spec);
329 empathy_dispatch_operation_dispose (GObject *object)
331 EmpathyDispatchOperation *self = EMPATHY_DISPATCH_OPERATION (object);
332 EmpathyDispatchOperationPriv *priv =
335 if (priv->dispose_has_run)
338 priv->dispose_has_run = TRUE;
340 g_object_unref (priv->connection);
342 if (priv->channel_wrapper != NULL)
343 g_object_unref (priv->channel_wrapper);
345 if (priv->ready_handler != 0)
346 g_signal_handler_disconnect (priv->channel_wrapper,
347 priv->invalidated_handler);
350 g_signal_handler_disconnect (priv->channel, priv->invalidated_handler);
351 g_object_unref (priv->channel);
354 if (priv->contact != NULL)
355 g_object_unref (priv->contact);
357 if (G_OBJECT_CLASS (empathy_dispatch_operation_parent_class)->dispose)
358 G_OBJECT_CLASS (empathy_dispatch_operation_parent_class)->dispose (object);
362 empathy_dispatch_operation_finalize (GObject *object)
364 /* free any data held directly by the object here */
365 G_OBJECT_CLASS (empathy_dispatch_operation_parent_class)->finalize (object);
369 empathy_dispatch_operation_set_status (EmpathyDispatchOperation *self,
370 EmpathyDispatchOperationState status)
372 EmpathyDispatchOperationPriv *priv = GET_PRIV (self);
374 g_assert (status >= priv->status);
377 if (priv->status != status)
379 DEBUG ("Dispatch operation %s status: %d -> %d",
380 empathy_dispatch_operation_get_object_path (self),
381 priv->status, status);
383 priv->status = status;
384 g_object_notify (G_OBJECT (self), "status");
386 if (status == EMPATHY_DISPATCHER_OPERATION_STATE_PENDING)
387 g_signal_emit (self, signals[READY], 0);
392 empathy_dispatcher_operation_tp_chat_ready_cb (GObject *object,
393 GParamSpec *spec, gpointer user_data)
395 EmpathyDispatchOperation *self = EMPATHY_DISPATCH_OPERATION (user_data);
396 EmpathyDispatchOperationPriv *priv = GET_PRIV (self);
398 if (!empathy_tp_chat_is_ready (EMPATHY_TP_CHAT (priv->channel_wrapper)))
401 g_signal_handler_disconnect (priv->channel_wrapper, priv->ready_handler);
402 priv->ready_handler = 0;
404 empathy_dispatch_operation_set_status (self,
405 EMPATHY_DISPATCHER_OPERATION_STATE_PENDING);
409 empathy_dispatcher_operation_tp_file_ready_cb (GObject *object,
410 GParamSpec *spec, gpointer user_data)
412 EmpathyDispatchOperation *self = EMPATHY_DISPATCH_OPERATION (user_data);
413 EmpathyDispatchOperationPriv *priv = GET_PRIV (self);
415 if (!empathy_tp_file_is_ready (EMPATHY_TP_FILE (priv->channel_wrapper)))
418 g_signal_handler_disconnect (priv->channel_wrapper, priv->ready_handler);
419 priv->ready_handler = 0;
421 empathy_dispatch_operation_set_status (self,
422 EMPATHY_DISPATCHER_OPERATION_STATE_PENDING);
426 empathy_dispatch_operation_channel_ready_cb (TpChannel *channel,
427 const GError *error, gpointer user_data)
429 EmpathyDispatchOperation *self = EMPATHY_DISPATCH_OPERATION (user_data);
430 EmpathyDispatchOperationPriv *priv = GET_PRIV (self);
433 /* The error will be handled in empathy_dispatch_operation_invalidated */
437 g_assert (channel == priv->channel);
439 /* If the channel wrapper is defined, we assume it's ready */
440 if (priv->channel_wrapper != NULL)
443 channel_type = tp_channel_get_channel_type_id (channel);
445 if (channel_type == TP_IFACE_QUARK_CHANNEL_TYPE_TEXT)
447 EmpathyTpChat *chat= empathy_tp_chat_new (channel);
448 priv->channel_wrapper = G_OBJECT (chat);
450 if (!empathy_tp_chat_is_ready (chat))
452 priv->ready_handler = g_signal_connect (chat, "notify::ready",
453 G_CALLBACK (empathy_dispatcher_operation_tp_chat_ready_cb), self);
457 else if (channel_type == TP_IFACE_QUARK_CHANNEL_TYPE_STREAMED_MEDIA)
459 EmpathyTpCall *call = empathy_tp_call_new (channel);
460 priv->channel_wrapper = G_OBJECT (call);
462 else if (channel_type == TP_IFACE_QUARK_CHANNEL_TYPE_FILE_TRANSFER)
464 EmpathyTpFile *file = empathy_tp_file_new (channel);
465 priv->channel_wrapper = G_OBJECT (file);
467 if (!empathy_tp_file_is_ready (file))
469 priv->ready_handler = g_signal_connect (file, "notify::ready",
470 G_CALLBACK (empathy_dispatcher_operation_tp_file_ready_cb), self);
476 empathy_dispatch_operation_set_status (self,
477 EMPATHY_DISPATCHER_OPERATION_STATE_PENDING);
480 EmpathyDispatchOperation *
481 empathy_dispatch_operation_new (TpConnection *connection, TpChannel *channel,
482 EmpathyContact *contact, gboolean incoming)
484 return empathy_dispatch_operation_new_with_wrapper (connection, channel,
485 contact, incoming, NULL);
488 EmpathyDispatchOperation *
489 empathy_dispatch_operation_new_with_wrapper (TpConnection *connection,
490 TpChannel *channel, EmpathyContact *contact, gboolean incoming,
493 g_return_val_if_fail (connection != NULL, NULL);
494 g_return_val_if_fail (channel != NULL, NULL);
496 return EMPATHY_DISPATCH_OPERATION (
497 g_object_new (EMPATHY_TYPE_DISPATCH_OPERATION,
498 "connection", connection,
500 "channel-wrapper", wrapper,
502 "incoming", incoming,
507 empathy_dispatch_operation_start (EmpathyDispatchOperation *operation)
509 EmpathyDispatchOperationPriv *priv;
511 g_return_if_fail (EMPATHY_IS_DISPATCH_OPERATION (operation));
513 priv = GET_PRIV (operation);
516 priv->status == EMPATHY_DISPATCHER_OPERATION_STATE_PENDING);
518 if (priv->incoming && !priv->approved)
519 empathy_dispatch_operation_set_status (operation,
520 EMPATHY_DISPATCHER_OPERATION_STATE_APPROVING);
522 empathy_dispatch_operation_set_status (operation,
523 EMPATHY_DISPATCHER_OPERATION_STATE_DISPATCHING);
527 empathy_dispatch_operation_approve (EmpathyDispatchOperation *operation)
529 EmpathyDispatchOperationPriv *priv;
531 g_return_if_fail (EMPATHY_IS_DISPATCH_OPERATION (operation));
533 priv = GET_PRIV (operation);
535 if (priv->status == EMPATHY_DISPATCHER_OPERATION_STATE_APPROVING)
537 DEBUG ("Approving operation %s",
538 empathy_dispatch_operation_get_object_path (operation));
540 empathy_dispatch_operation_set_status (operation,
541 EMPATHY_DISPATCHER_OPERATION_STATE_DISPATCHING);
543 g_signal_emit (operation, signals[APPROVED], 0);
545 else if (priv->status < EMPATHY_DISPATCHER_OPERATION_STATE_APPROVING)
547 DEBUG ("Pre-approving operation %s",
548 empathy_dispatch_operation_get_object_path (operation));
549 priv->approved = TRUE;
554 "Ignoring approval for %s as it's already past the approval stage",
555 empathy_dispatch_operation_get_object_path (operation));
559 /* Returns whether or not the operation was successfully claimed */
561 empathy_dispatch_operation_claim (EmpathyDispatchOperation *operation)
563 EmpathyDispatchOperationPriv *priv;
565 g_return_val_if_fail (EMPATHY_IS_DISPATCH_OPERATION (operation), FALSE);
567 priv = GET_PRIV (operation);
569 if (priv->status == EMPATHY_DISPATCHER_OPERATION_STATE_CLAIMED)
572 empathy_dispatch_operation_set_status (operation,
573 EMPATHY_DISPATCHER_OPERATION_STATE_CLAIMED);
575 g_signal_emit (operation, signals[CLAIMED], 0);
581 empathy_dispatch_operation_get_tp_connection (
582 EmpathyDispatchOperation *operation)
584 EmpathyDispatchOperationPriv *priv;
586 g_return_val_if_fail (EMPATHY_IS_DISPATCH_OPERATION (operation), NULL);
588 priv = GET_PRIV (operation);
590 return g_object_ref (priv->connection);
594 empathy_dispatch_operation_get_channel (EmpathyDispatchOperation *operation)
596 EmpathyDispatchOperationPriv *priv;
598 g_return_val_if_fail (EMPATHY_IS_DISPATCH_OPERATION (operation), NULL);
600 priv = GET_PRIV (operation);
602 return priv->channel;
606 empathy_dispatch_operation_get_channel_wrapper (
607 EmpathyDispatchOperation *operation)
609 EmpathyDispatchOperationPriv *priv;
611 g_return_val_if_fail (EMPATHY_IS_DISPATCH_OPERATION (operation), NULL);
613 priv = GET_PRIV (operation);
615 return priv->channel_wrapper;
619 empathy_dispatch_operation_get_channel_type (
620 EmpathyDispatchOperation *operation)
622 EmpathyDispatchOperationPriv *priv;
624 g_return_val_if_fail (EMPATHY_IS_DISPATCH_OPERATION (operation), NULL);
626 priv = GET_PRIV (operation);
628 return tp_channel_get_channel_type (priv->channel);
632 empathy_dispatch_operation_get_channel_type_id (
633 EmpathyDispatchOperation *operation)
635 EmpathyDispatchOperationPriv *priv;
637 g_return_val_if_fail (EMPATHY_IS_DISPATCH_OPERATION (operation), 0);
639 priv = GET_PRIV (operation);
641 return tp_channel_get_channel_type_id (priv->channel);
645 empathy_dispatch_operation_get_object_path (
646 EmpathyDispatchOperation *operation)
648 EmpathyDispatchOperationPriv *priv;
650 g_return_val_if_fail (EMPATHY_IS_DISPATCH_OPERATION (operation), NULL);
652 priv = GET_PRIV (operation);
654 return tp_proxy_get_object_path (TP_PROXY (priv->channel));
657 EmpathyDispatchOperationState
658 empathy_dispatch_operation_get_status (EmpathyDispatchOperation *operation)
660 EmpathyDispatchOperationPriv *priv;
662 g_return_val_if_fail (EMPATHY_IS_DISPATCH_OPERATION (operation),
663 EMPATHY_DISPATCHER_OPERATION_STATE_PREPARING);
665 priv = GET_PRIV (operation);
671 empathy_dispatch_operation_is_incoming (EmpathyDispatchOperation *operation)
673 EmpathyDispatchOperationPriv *priv;
675 g_return_val_if_fail (EMPATHY_IS_DISPATCH_OPERATION (operation), FALSE);
677 priv = GET_PRIV (operation);
679 return priv->incoming;