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 <telepathy-glib/interfaces.h>
27 #include "empathy-dispatch-operation.h"
28 #include <libempathy/empathy-enum-types.h>
29 #include <libempathy/empathy-tp-contact-factory.h>
30 #include <libempathy/empathy-tp-chat.h>
31 #include <libempathy/empathy-tp-call.h>
32 #include <libempathy/empathy-tp-file.h>
34 #include "empathy-marshal.h"
36 #include "extensions/extensions.h"
38 #define DEBUG_FLAG EMPATHY_DEBUG_DISPATCHER
39 #include <libempathy/empathy-debug.h>
41 G_DEFINE_TYPE(EmpathyDispatchOperation, empathy_dispatch_operation,
44 static void empathy_dispatch_operation_set_status (
45 EmpathyDispatchOperation *self, EmpathyDispatchOperationState status);
46 static void empathy_dispatch_operation_channel_ready_cb (TpChannel *channel,
47 const GError *error, gpointer user_data);
52 /* Ready for dispatching */
54 /* Approved by an approver, can only happens on incoming operations */
56 /* Claimed by a handler */
58 /* Error, channel went away, inspecting it failed etc */
63 static guint signals[LAST_SIGNAL] = {0};
75 /* private structure */
76 typedef struct _EmpathyDispatchOperationPriv \
77 EmpathyDispatchOperationPriv;
79 struct _EmpathyDispatchOperationPriv
81 gboolean dispose_has_run;
82 TpConnection *connection;
84 GObject *channel_wrapper;
85 EmpathyContact *contact;
86 EmpathyDispatchOperationState status;
89 gulong invalidated_handler;
94 (G_TYPE_INSTANCE_GET_PRIVATE ((o), EMPATHY_TYPE_DISPATCH_OPERATION, \
95 EmpathyDispatchOperationPriv))
98 empathy_dispatch_operation_init (EmpathyDispatchOperation *obj)
100 //EmpathyDispatchOperationPriv *priv =
103 /* allocate any data required by the object here */
106 static void empathy_dispatch_operation_dispose (GObject *object);
107 static void empathy_dispatch_operation_finalize (GObject *object);
110 empathy_dispatch_operation_set_property (GObject *object,
111 guint property_id, const GValue *value, GParamSpec *pspec)
113 EmpathyDispatchOperation *operation = EMPATHY_DISPATCH_OPERATION (object);
114 EmpathyDispatchOperationPriv *priv = GET_PRIV (operation);
118 case PROP_CONNECTION:
119 priv->connection = g_value_dup_object (value);
122 priv->channel = g_value_dup_object (value);
124 case PROP_CHANNEL_WRAPPER:
125 priv->channel_wrapper = g_value_dup_object (value);
128 if (priv->contact != NULL)
129 g_object_unref (priv->contact);
130 priv->contact = g_value_dup_object (value);
133 priv->incoming = g_value_get_boolean (value);
139 empathy_dispatch_operation_get_property (GObject *object,
140 guint property_id, GValue *value, GParamSpec *pspec)
142 EmpathyDispatchOperation *operation = EMPATHY_DISPATCH_OPERATION (object);
143 EmpathyDispatchOperationPriv *priv = GET_PRIV (operation);
147 case PROP_CONNECTION:
148 g_value_set_object (value, priv->connection);
151 g_value_set_object (value, priv->channel);
153 case PROP_CHANNEL_WRAPPER:
154 g_value_set_object (value, priv->channel_wrapper);
157 g_value_set_object (value, priv->contact);
160 g_value_set_boolean (value, priv->incoming);
163 g_value_set_enum (value, priv->status);
169 empathy_dispatch_operation_invalidated (TpProxy *proxy, guint domain,
170 gint code, char *message, EmpathyDispatchOperation *self)
172 empathy_dispatch_operation_set_status (self,
173 EMPATHY_DISPATCHER_OPERATION_STATE_INVALIDATED);
175 g_signal_emit (self, signals[INVALIDATED], 0, domain, code, message);
179 dispatcher_operation_got_contact_cb (EmpathyTpContactFactory *factory,
180 EmpathyContact *contact,
185 EmpathyDispatchOperationPriv *priv = GET_PRIV (self);
189 /* FIXME: We should cancel the operation */
190 DEBUG ("Error: %s", error->message);
194 if (priv->contact != NULL)
195 g_object_unref (priv->contact);
196 priv->contact = g_object_ref (contact);
197 g_object_notify (G_OBJECT (self), "contact");
199 tp_channel_call_when_ready (priv->channel,
200 empathy_dispatch_operation_channel_ready_cb, self);
204 dispatch_operation_connection_ready (TpConnection *connection,
208 EmpathyDispatchOperation *self = EMPATHY_DISPATCH_OPERATION (user_data);
209 EmpathyDispatchOperationPriv *priv = GET_PRIV (self);
210 EmpathyTpContactFactory *factory;
213 handle = tp_channel_get_handle (priv->channel, NULL);
215 factory = empathy_tp_contact_factory_dup_singleton (priv->connection);
217 empathy_tp_contact_factory_get_from_handle (factory, handle,
218 dispatcher_operation_got_contact_cb, NULL, NULL, G_OBJECT (self));
220 g_object_unref (factory);
224 empathy_dispatch_operation_constructed (GObject *object)
226 EmpathyDispatchOperation *self = EMPATHY_DISPATCH_OPERATION (object);
227 EmpathyDispatchOperationPriv *priv = GET_PRIV (self);
229 TpHandleType handle_type;
231 empathy_dispatch_operation_set_status (self,
232 EMPATHY_DISPATCHER_OPERATION_STATE_PREPARING);
234 priv->invalidated_handler =
235 g_signal_connect (priv->channel, "invalidated",
236 G_CALLBACK (empathy_dispatch_operation_invalidated), self);
238 handle = tp_channel_get_handle (priv->channel, &handle_type);
240 if (handle_type == TP_HANDLE_TYPE_CONTACT && priv->contact == NULL)
242 tp_connection_call_when_ready (priv->connection,
243 dispatch_operation_connection_ready, object);
247 tp_channel_call_when_ready (priv->channel,
248 empathy_dispatch_operation_channel_ready_cb, self);
252 empathy_dispatch_operation_class_init (
253 EmpathyDispatchOperationClass *empathy_dispatch_operation_class)
255 GObjectClass *object_class =
256 G_OBJECT_CLASS (empathy_dispatch_operation_class);
257 GParamSpec *param_spec;
259 g_type_class_add_private (empathy_dispatch_operation_class,
260 sizeof (EmpathyDispatchOperationPriv));
262 object_class->set_property = empathy_dispatch_operation_set_property;
263 object_class->get_property = empathy_dispatch_operation_get_property;
265 object_class->dispose = empathy_dispatch_operation_dispose;
266 object_class->finalize = empathy_dispatch_operation_finalize;
267 object_class->constructed = empathy_dispatch_operation_constructed;
269 signals[READY] = g_signal_new ("ready",
270 G_OBJECT_CLASS_TYPE(empathy_dispatch_operation_class),
274 g_cclosure_marshal_VOID__VOID,
277 signals[APPROVED] = g_signal_new ("approved",
278 G_OBJECT_CLASS_TYPE(empathy_dispatch_operation_class),
282 g_cclosure_marshal_VOID__VOID,
285 signals[CLAIMED] = g_signal_new ("claimed",
286 G_OBJECT_CLASS_TYPE(empathy_dispatch_operation_class),
290 g_cclosure_marshal_VOID__VOID,
293 signals[INVALIDATED] = g_signal_new ("invalidated",
294 G_OBJECT_CLASS_TYPE(empathy_dispatch_operation_class),
298 _empathy_marshal_VOID__UINT_INT_STRING,
299 G_TYPE_NONE, 3, G_TYPE_UINT, G_TYPE_INT, G_TYPE_STRING);
301 param_spec = g_param_spec_object ("connection",
302 "connection", "The telepathy connection",
304 G_PARAM_CONSTRUCT_ONLY |
305 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
306 g_object_class_install_property (object_class, PROP_CONNECTION,
309 param_spec = g_param_spec_object ("channel",
310 "channel", "The telepathy channel",
312 G_PARAM_CONSTRUCT_ONLY |
313 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
314 g_object_class_install_property (object_class, PROP_CHANNEL,
317 param_spec = g_param_spec_object ("channel-wrapper",
318 "channel wrapper", "The empathy specific channel wrapper",
320 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
321 g_object_class_install_property (object_class, PROP_CHANNEL_WRAPPER,
324 param_spec = g_param_spec_object ("contact",
325 "contact", "The empathy contact",
326 EMPATHY_TYPE_CONTACT,
327 G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
328 g_object_class_install_property (object_class, PROP_CONTACT,
331 param_spec = g_param_spec_boolean ("incoming",
332 "incoming", "Whether or not the channel is incoming",
334 G_PARAM_CONSTRUCT_ONLY |
335 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
336 g_object_class_install_property (object_class, PROP_INCOMING,
339 param_spec = g_param_spec_enum ("status",
340 "status", "Status of the dispatch operation",
341 EMPATHY_TYPE_DISPATCH_OPERATION_STATE, 0,
342 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
343 g_object_class_install_property (object_class, PROP_STATUS, param_spec);
347 empathy_dispatch_operation_dispose (GObject *object)
349 EmpathyDispatchOperation *self = EMPATHY_DISPATCH_OPERATION (object);
350 EmpathyDispatchOperationPriv *priv =
353 if (priv->dispose_has_run)
356 priv->dispose_has_run = TRUE;
358 g_object_unref (priv->connection);
360 if (priv->channel_wrapper != NULL)
361 g_object_unref (priv->channel_wrapper);
363 if (priv->ready_handler != 0)
364 g_signal_handler_disconnect (priv->channel_wrapper,
365 priv->invalidated_handler);
368 g_signal_handler_disconnect (priv->channel, priv->invalidated_handler);
369 g_object_unref (priv->channel);
372 if (priv->contact != NULL)
373 g_object_unref (priv->contact);
375 if (G_OBJECT_CLASS (empathy_dispatch_operation_parent_class)->dispose)
376 G_OBJECT_CLASS (empathy_dispatch_operation_parent_class)->dispose (object);
380 empathy_dispatch_operation_finalize (GObject *object)
382 /* free any data held directly by the object here */
383 G_OBJECT_CLASS (empathy_dispatch_operation_parent_class)->finalize (object);
387 empathy_dispatch_operation_set_status (EmpathyDispatchOperation *self,
388 EmpathyDispatchOperationState status)
390 EmpathyDispatchOperationPriv *priv = GET_PRIV (self);
392 g_assert (status >= priv->status);
395 if (priv->status != status)
397 DEBUG ("Dispatch operation %s status: %d -> %d",
398 empathy_dispatch_operation_get_object_path (self),
399 priv->status, status);
401 priv->status = status;
402 g_object_notify (G_OBJECT (self), "status");
404 if (status == EMPATHY_DISPATCHER_OPERATION_STATE_PENDING)
405 g_signal_emit (self, signals[READY], 0);
410 empathy_dispatcher_operation_tp_chat_ready_cb (GObject *object,
411 GParamSpec *spec, gpointer user_data)
413 EmpathyDispatchOperation *self = EMPATHY_DISPATCH_OPERATION (user_data);
414 EmpathyDispatchOperationPriv *priv = GET_PRIV (self);
416 if (!empathy_tp_chat_is_ready (EMPATHY_TP_CHAT (priv->channel_wrapper)))
419 g_signal_handler_disconnect (priv->channel_wrapper, priv->ready_handler);
420 priv->ready_handler = 0;
422 empathy_dispatch_operation_set_status (self,
423 EMPATHY_DISPATCHER_OPERATION_STATE_PENDING);
427 empathy_dispatch_operation_channel_ready_cb (TpChannel *channel,
428 const GError *error, gpointer user_data)
430 EmpathyDispatchOperation *self = EMPATHY_DISPATCH_OPERATION (user_data);
431 EmpathyDispatchOperationPriv *priv = GET_PRIV (self);
434 /* The error will be handled in empathy_dispatch_operation_invalidated */
438 g_assert (channel == priv->channel);
440 /* If the channel wrapper is defined, we assume it's ready */
441 if (priv->channel_wrapper != NULL)
444 channel_type = tp_channel_get_channel_type_id (channel);
446 if (channel_type == TP_IFACE_QUARK_CHANNEL_TYPE_TEXT)
448 EmpathyTpChat *chat= empathy_tp_chat_new (channel);
449 priv->channel_wrapper = G_OBJECT (chat);
451 if (!empathy_tp_chat_is_ready (chat))
453 priv->ready_handler = g_signal_connect (chat, "notify::ready",
454 G_CALLBACK (empathy_dispatcher_operation_tp_chat_ready_cb), self);
458 else if (channel_type == TP_IFACE_QUARK_CHANNEL_TYPE_STREAMED_MEDIA)
460 EmpathyTpCall *call = empathy_tp_call_new (channel);
461 priv->channel_wrapper = G_OBJECT (call);
463 else if (channel_type == TP_IFACE_QUARK_CHANNEL_TYPE_FILE_TRANSFER)
465 EmpathyTpFile *file = empathy_tp_file_new (channel, priv->incoming);
466 priv->channel_wrapper = G_OBJECT (file);
470 empathy_dispatch_operation_set_status (self,
471 EMPATHY_DISPATCHER_OPERATION_STATE_PENDING);
474 EmpathyDispatchOperation *
475 empathy_dispatch_operation_new (TpConnection *connection, TpChannel *channel,
476 EmpathyContact *contact, gboolean incoming)
478 return empathy_dispatch_operation_new_with_wrapper (connection, channel,
479 contact, incoming, NULL);
482 EmpathyDispatchOperation *
483 empathy_dispatch_operation_new_with_wrapper (TpConnection *connection,
484 TpChannel *channel, EmpathyContact *contact, gboolean incoming,
487 g_return_val_if_fail (connection != NULL, NULL);
488 g_return_val_if_fail (channel != NULL, NULL);
490 return EMPATHY_DISPATCH_OPERATION (
491 g_object_new (EMPATHY_TYPE_DISPATCH_OPERATION,
492 "connection", connection,
494 "channel-wrapper", wrapper,
496 "incoming", incoming,
501 empathy_dispatch_operation_start (EmpathyDispatchOperation *operation)
503 EmpathyDispatchOperationPriv *priv;
505 g_return_if_fail (EMPATHY_IS_DISPATCH_OPERATION (operation));
507 priv = GET_PRIV (operation);
510 priv->status == EMPATHY_DISPATCHER_OPERATION_STATE_PENDING);
512 if (priv->incoming && !priv->approved)
513 empathy_dispatch_operation_set_status (operation,
514 EMPATHY_DISPATCHER_OPERATION_STATE_APPROVING);
516 empathy_dispatch_operation_set_status (operation,
517 EMPATHY_DISPATCHER_OPERATION_STATE_DISPATCHING);
521 empathy_dispatch_operation_approve (EmpathyDispatchOperation *operation)
523 EmpathyDispatchOperationPriv *priv;
525 g_return_if_fail (EMPATHY_IS_DISPATCH_OPERATION (operation));
527 priv = GET_PRIV (operation);
529 if (priv->status == EMPATHY_DISPATCHER_OPERATION_STATE_APPROVING)
531 DEBUG ("Approving operation %s",
532 empathy_dispatch_operation_get_object_path (operation));
534 empathy_dispatch_operation_set_status (operation,
535 EMPATHY_DISPATCHER_OPERATION_STATE_DISPATCHING);
537 g_signal_emit (operation, signals[APPROVED], 0);
539 else if (priv->status < EMPATHY_DISPATCHER_OPERATION_STATE_APPROVING)
541 DEBUG ("Pre-approving operation %s",
542 empathy_dispatch_operation_get_object_path (operation));
543 priv->approved = TRUE;
548 "Ignoring approval for %s as it's already past the approval stage",
549 empathy_dispatch_operation_get_object_path (operation));
553 /* Returns whether or not the operation was successfully claimed */
555 empathy_dispatch_operation_claim (EmpathyDispatchOperation *operation)
557 EmpathyDispatchOperationPriv *priv;
559 g_return_val_if_fail (EMPATHY_IS_DISPATCH_OPERATION (operation), FALSE);
561 priv = GET_PRIV (operation);
563 if (priv->status == EMPATHY_DISPATCHER_OPERATION_STATE_CLAIMED)
566 empathy_dispatch_operation_set_status (operation,
567 EMPATHY_DISPATCHER_OPERATION_STATE_CLAIMED);
569 g_signal_emit (operation, signals[CLAIMED], 0);
575 empathy_dispatch_operation_get_tp_connection (
576 EmpathyDispatchOperation *operation)
578 EmpathyDispatchOperationPriv *priv;
580 g_return_val_if_fail (EMPATHY_IS_DISPATCH_OPERATION (operation), NULL);
582 priv = GET_PRIV (operation);
584 return priv->connection;
588 empathy_dispatch_operation_get_channel (EmpathyDispatchOperation *operation)
590 EmpathyDispatchOperationPriv *priv;
592 g_return_val_if_fail (EMPATHY_IS_DISPATCH_OPERATION (operation), NULL);
594 priv = GET_PRIV (operation);
596 return priv->channel;
600 empathy_dispatch_operation_get_channel_wrapper (
601 EmpathyDispatchOperation *operation)
603 EmpathyDispatchOperationPriv *priv;
605 g_return_val_if_fail (EMPATHY_IS_DISPATCH_OPERATION (operation), NULL);
607 priv = GET_PRIV (operation);
609 return priv->channel_wrapper;
613 empathy_dispatch_operation_get_channel_type (
614 EmpathyDispatchOperation *operation)
616 EmpathyDispatchOperationPriv *priv;
618 g_return_val_if_fail (EMPATHY_IS_DISPATCH_OPERATION (operation), NULL);
620 priv = GET_PRIV (operation);
622 return tp_channel_get_channel_type (priv->channel);
626 empathy_dispatch_operation_get_channel_type_id (
627 EmpathyDispatchOperation *operation)
629 EmpathyDispatchOperationPriv *priv;
631 g_return_val_if_fail (EMPATHY_IS_DISPATCH_OPERATION (operation), 0);
633 priv = GET_PRIV (operation);
635 return tp_channel_get_channel_type_id (priv->channel);
639 empathy_dispatch_operation_get_object_path (
640 EmpathyDispatchOperation *operation)
642 EmpathyDispatchOperationPriv *priv;
644 g_return_val_if_fail (EMPATHY_IS_DISPATCH_OPERATION (operation), NULL);
646 priv = GET_PRIV (operation);
648 return tp_proxy_get_object_path (TP_PROXY (priv->channel));
651 EmpathyDispatchOperationState
652 empathy_dispatch_operation_get_status (EmpathyDispatchOperation *operation)
654 EmpathyDispatchOperationPriv *priv;
656 g_return_val_if_fail (EMPATHY_IS_DISPATCH_OPERATION (operation),
657 EMPATHY_DISPATCHER_OPERATION_STATE_PREPARING);
659 priv = GET_PRIV (operation);
665 empathy_dispatch_operation_is_incoming (EmpathyDispatchOperation *operation)
667 EmpathyDispatchOperationPriv *priv;
669 g_return_val_if_fail (EMPATHY_IS_DISPATCH_OPERATION (operation), FALSE);
671 priv = GET_PRIV (operation);
673 return priv->incoming;