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 /* Ensure to keep the self object alive while the call_when_ready is
202 tp_channel_call_when_ready (priv
->channel
,
203 empathy_dispatch_operation_channel_ready_cb
, self
);
207 dispatch_operation_connection_ready (TpConnection
*connection
,
211 EmpathyDispatchOperation
*self
= EMPATHY_DISPATCH_OPERATION (user_data
);
212 EmpathyDispatchOperationPriv
*priv
= GET_PRIV (self
);
213 EmpathyTpContactFactory
*factory
;
219 if (priv
->status
>= EMPATHY_DISPATCHER_OPERATION_STATE_CLAIMED
)
220 /* no point to get more information */
223 handle
= tp_channel_get_handle (priv
->channel
, NULL
);
225 factory
= empathy_tp_contact_factory_dup_singleton (priv
->connection
);
227 empathy_tp_contact_factory_get_from_handle (factory
, handle
,
228 dispatcher_operation_got_contact_cb
, NULL
, NULL
, G_OBJECT (self
));
230 g_object_unref (factory
);
232 g_object_unref (self
);
236 empathy_dispatch_operation_constructed (GObject
*object
)
238 EmpathyDispatchOperation
*self
= EMPATHY_DISPATCH_OPERATION (object
);
239 EmpathyDispatchOperationPriv
*priv
= GET_PRIV (self
);
241 TpHandleType handle_type
;
243 empathy_dispatch_operation_set_status (self
,
244 EMPATHY_DISPATCHER_OPERATION_STATE_PREPARING
);
246 priv
->invalidated_handler
=
247 g_signal_connect (priv
->channel
, "invalidated",
248 G_CALLBACK (empathy_dispatch_operation_invalidated
), self
);
250 handle
= tp_channel_get_handle (priv
->channel
, &handle_type
);
252 if (handle_type
== TP_HANDLE_TYPE_CONTACT
&& priv
->contact
== NULL
)
254 /* Ensure to keep the self object alive while the call_when_ready is
257 tp_connection_call_when_ready (priv
->connection
,
258 dispatch_operation_connection_ready
, object
);
263 tp_channel_call_when_ready (priv
->channel
,
264 empathy_dispatch_operation_channel_ready_cb
, self
);
268 empathy_dispatch_operation_class_init (
269 EmpathyDispatchOperationClass
*empathy_dispatch_operation_class
)
271 GObjectClass
*object_class
=
272 G_OBJECT_CLASS (empathy_dispatch_operation_class
);
273 GParamSpec
*param_spec
;
275 g_type_class_add_private (empathy_dispatch_operation_class
,
276 sizeof (EmpathyDispatchOperationPriv
));
278 object_class
->set_property
= empathy_dispatch_operation_set_property
;
279 object_class
->get_property
= empathy_dispatch_operation_get_property
;
281 object_class
->dispose
= empathy_dispatch_operation_dispose
;
282 object_class
->finalize
= empathy_dispatch_operation_finalize
;
283 object_class
->constructed
= empathy_dispatch_operation_constructed
;
285 signals
[READY
] = g_signal_new ("ready",
286 G_OBJECT_CLASS_TYPE(empathy_dispatch_operation_class
),
290 g_cclosure_marshal_VOID__VOID
,
293 signals
[APPROVED
] = g_signal_new ("approved",
294 G_OBJECT_CLASS_TYPE(empathy_dispatch_operation_class
),
298 g_cclosure_marshal_VOID__VOID
,
301 signals
[CLAIMED
] = g_signal_new ("claimed",
302 G_OBJECT_CLASS_TYPE(empathy_dispatch_operation_class
),
306 g_cclosure_marshal_VOID__VOID
,
309 signals
[INVALIDATED
] = g_signal_new ("invalidated",
310 G_OBJECT_CLASS_TYPE(empathy_dispatch_operation_class
),
314 _empathy_marshal_VOID__UINT_INT_STRING
,
315 G_TYPE_NONE
, 3, G_TYPE_UINT
, G_TYPE_INT
, G_TYPE_STRING
);
317 param_spec
= g_param_spec_object ("connection",
318 "connection", "The telepathy connection",
320 G_PARAM_CONSTRUCT_ONLY
|
321 G_PARAM_READWRITE
| G_PARAM_STATIC_STRINGS
);
322 g_object_class_install_property (object_class
, PROP_CONNECTION
,
325 param_spec
= g_param_spec_object ("channel",
326 "channel", "The telepathy channel",
328 G_PARAM_CONSTRUCT_ONLY
|
329 G_PARAM_READWRITE
| G_PARAM_STATIC_STRINGS
);
330 g_object_class_install_property (object_class
, PROP_CHANNEL
,
333 param_spec
= g_param_spec_object ("channel-wrapper",
334 "channel wrapper", "The empathy specific channel wrapper",
336 G_PARAM_CONSTRUCT_ONLY
| G_PARAM_READWRITE
| G_PARAM_STATIC_STRINGS
);
337 g_object_class_install_property (object_class
, PROP_CHANNEL_WRAPPER
,
340 param_spec
= g_param_spec_object ("contact",
341 "contact", "The empathy contact",
342 EMPATHY_TYPE_CONTACT
,
343 G_PARAM_CONSTRUCT
| G_PARAM_READWRITE
| G_PARAM_STATIC_STRINGS
);
344 g_object_class_install_property (object_class
, PROP_CONTACT
,
347 param_spec
= g_param_spec_boolean ("incoming",
348 "incoming", "Whether or not the channel is incoming",
350 G_PARAM_CONSTRUCT_ONLY
|
351 G_PARAM_READWRITE
| G_PARAM_STATIC_STRINGS
);
352 g_object_class_install_property (object_class
, PROP_INCOMING
,
355 param_spec
= g_param_spec_enum ("status",
356 "status", "Status of the dispatch operation",
357 EMPATHY_TYPE_DISPATCH_OPERATION_STATE
, 0,
358 G_PARAM_READABLE
| G_PARAM_STATIC_STRINGS
);
359 g_object_class_install_property (object_class
, PROP_STATUS
, param_spec
);
363 empathy_dispatch_operation_dispose (GObject
*object
)
365 EmpathyDispatchOperation
*self
= EMPATHY_DISPATCH_OPERATION (object
);
366 EmpathyDispatchOperationPriv
*priv
=
369 if (priv
->dispose_has_run
)
372 priv
->dispose_has_run
= TRUE
;
374 g_object_unref (priv
->connection
);
376 if (priv
->ready_handler
!= 0)
377 g_signal_handler_disconnect (priv
->channel_wrapper
,
378 priv
->ready_handler
);
380 if (priv
->channel_wrapper
!= NULL
)
381 g_object_unref (priv
->channel_wrapper
);
383 g_signal_handler_disconnect (priv
->channel
, priv
->invalidated_handler
);
384 g_object_unref (priv
->channel
);
386 if (priv
->contact
!= NULL
)
387 g_object_unref (priv
->contact
);
389 if (G_OBJECT_CLASS (empathy_dispatch_operation_parent_class
)->dispose
)
390 G_OBJECT_CLASS (empathy_dispatch_operation_parent_class
)->dispose (object
);
394 empathy_dispatch_operation_finalize (GObject
*object
)
396 /* free any data held directly by the object here */
397 G_OBJECT_CLASS (empathy_dispatch_operation_parent_class
)->finalize (object
);
401 empathy_dispatch_operation_set_status (EmpathyDispatchOperation
*self
,
402 EmpathyDispatchOperationState status
)
404 EmpathyDispatchOperationPriv
*priv
= GET_PRIV (self
);
406 g_assert (status
>= priv
->status
);
409 if (priv
->status
!= status
)
411 DEBUG ("Dispatch operation %s status: %d -> %d",
412 empathy_dispatch_operation_get_object_path (self
),
413 priv
->status
, status
);
415 priv
->status
= status
;
416 g_object_notify (G_OBJECT (self
), "status");
418 if (status
== EMPATHY_DISPATCHER_OPERATION_STATE_PENDING
)
419 g_signal_emit (self
, signals
[READY
], 0);
424 empathy_dispatcher_operation_tp_chat_ready_cb (GObject
*object
,
425 GParamSpec
*spec
, gpointer user_data
)
427 EmpathyDispatchOperation
*self
= EMPATHY_DISPATCH_OPERATION (user_data
);
428 EmpathyDispatchOperationPriv
*priv
= GET_PRIV (self
);
430 if (!empathy_tp_chat_is_ready (EMPATHY_TP_CHAT (priv
->channel_wrapper
)))
433 g_signal_handler_disconnect (priv
->channel_wrapper
, priv
->ready_handler
);
434 priv
->ready_handler
= 0;
436 empathy_dispatch_operation_set_status (self
,
437 EMPATHY_DISPATCHER_OPERATION_STATE_PENDING
);
441 empathy_dispatch_operation_channel_ready_cb (TpChannel
*channel
,
442 const GError
*error
, gpointer user_data
)
444 EmpathyDispatchOperation
*self
= EMPATHY_DISPATCH_OPERATION (user_data
);
445 EmpathyDispatchOperationPriv
*priv
= GET_PRIV (self
);
448 /* The error will be handled in empathy_dispatch_operation_invalidated */
452 g_assert (channel
== priv
->channel
);
454 if (priv
->status
>= EMPATHY_DISPATCHER_OPERATION_STATE_CLAIMED
)
455 /* no point to get more information */
458 /* If the channel wrapper is defined, we assume it's ready */
459 if (priv
->channel_wrapper
!= NULL
)
462 channel_type
= tp_channel_get_channel_type_id (channel
);
464 if (channel_type
== TP_IFACE_QUARK_CHANNEL_TYPE_TEXT
)
466 EmpathyTpChat
*chat
= empathy_tp_chat_new (channel
);
467 priv
->channel_wrapper
= G_OBJECT (chat
);
469 if (!empathy_tp_chat_is_ready (chat
))
471 priv
->ready_handler
= g_signal_connect (chat
, "notify::ready",
472 G_CALLBACK (empathy_dispatcher_operation_tp_chat_ready_cb
), self
);
476 else if (channel_type
== TP_IFACE_QUARK_CHANNEL_TYPE_STREAMED_MEDIA
)
478 EmpathyTpCall
*call
= empathy_tp_call_new (channel
);
479 priv
->channel_wrapper
= G_OBJECT (call
);
481 else if (channel_type
== TP_IFACE_QUARK_CHANNEL_TYPE_FILE_TRANSFER
)
483 EmpathyTpFile
*file
= empathy_tp_file_new (channel
, priv
->incoming
);
484 priv
->channel_wrapper
= G_OBJECT (file
);
488 empathy_dispatch_operation_set_status (self
,
489 EMPATHY_DISPATCHER_OPERATION_STATE_PENDING
);
491 g_object_unref (self
);
494 EmpathyDispatchOperation
*
495 empathy_dispatch_operation_new (TpConnection
*connection
, TpChannel
*channel
,
496 EmpathyContact
*contact
, gboolean incoming
)
498 return empathy_dispatch_operation_new_with_wrapper (connection
, channel
,
499 contact
, incoming
, NULL
);
502 EmpathyDispatchOperation
*
503 empathy_dispatch_operation_new_with_wrapper (TpConnection
*connection
,
504 TpChannel
*channel
, EmpathyContact
*contact
, gboolean incoming
,
507 g_return_val_if_fail (connection
!= NULL
, NULL
);
508 g_return_val_if_fail (channel
!= NULL
, NULL
);
510 return EMPATHY_DISPATCH_OPERATION (
511 g_object_new (EMPATHY_TYPE_DISPATCH_OPERATION
,
512 "connection", connection
,
514 "channel-wrapper", wrapper
,
516 "incoming", incoming
,
521 empathy_dispatch_operation_start (EmpathyDispatchOperation
*operation
)
523 EmpathyDispatchOperationPriv
*priv
;
525 g_return_if_fail (EMPATHY_IS_DISPATCH_OPERATION (operation
));
527 priv
= GET_PRIV (operation
);
530 priv
->status
== EMPATHY_DISPATCHER_OPERATION_STATE_PENDING
);
532 if (priv
->incoming
&& !priv
->approved
)
533 empathy_dispatch_operation_set_status (operation
,
534 EMPATHY_DISPATCHER_OPERATION_STATE_APPROVING
);
536 empathy_dispatch_operation_set_status (operation
,
537 EMPATHY_DISPATCHER_OPERATION_STATE_DISPATCHING
);
541 empathy_dispatch_operation_approve (EmpathyDispatchOperation
*operation
)
543 EmpathyDispatchOperationPriv
*priv
;
545 g_return_if_fail (EMPATHY_IS_DISPATCH_OPERATION (operation
));
547 priv
= GET_PRIV (operation
);
549 if (priv
->status
== EMPATHY_DISPATCHER_OPERATION_STATE_APPROVING
)
551 DEBUG ("Approving operation %s",
552 empathy_dispatch_operation_get_object_path (operation
));
554 empathy_dispatch_operation_set_status (operation
,
555 EMPATHY_DISPATCHER_OPERATION_STATE_DISPATCHING
);
557 g_signal_emit (operation
, signals
[APPROVED
], 0);
559 else if (priv
->status
< EMPATHY_DISPATCHER_OPERATION_STATE_APPROVING
)
561 DEBUG ("Pre-approving operation %s",
562 empathy_dispatch_operation_get_object_path (operation
));
563 priv
->approved
= TRUE
;
568 "Ignoring approval for %s as it's already past the approval stage",
569 empathy_dispatch_operation_get_object_path (operation
));
573 /* Returns whether or not the operation was successfully claimed */
575 empathy_dispatch_operation_claim (EmpathyDispatchOperation
*operation
)
577 EmpathyDispatchOperationPriv
*priv
;
579 g_return_val_if_fail (EMPATHY_IS_DISPATCH_OPERATION (operation
), FALSE
);
581 priv
= GET_PRIV (operation
);
583 if (priv
->status
== EMPATHY_DISPATCHER_OPERATION_STATE_CLAIMED
)
586 empathy_dispatch_operation_set_status (operation
,
587 EMPATHY_DISPATCHER_OPERATION_STATE_CLAIMED
);
589 g_signal_emit (operation
, signals
[CLAIMED
], 0);
595 empathy_dispatch_operation_get_tp_connection (
596 EmpathyDispatchOperation
*operation
)
598 EmpathyDispatchOperationPriv
*priv
;
600 g_return_val_if_fail (EMPATHY_IS_DISPATCH_OPERATION (operation
), NULL
);
602 priv
= GET_PRIV (operation
);
604 return priv
->connection
;
608 empathy_dispatch_operation_get_channel (EmpathyDispatchOperation
*operation
)
610 EmpathyDispatchOperationPriv
*priv
;
612 g_return_val_if_fail (EMPATHY_IS_DISPATCH_OPERATION (operation
), NULL
);
614 priv
= GET_PRIV (operation
);
616 return priv
->channel
;
620 empathy_dispatch_operation_get_channel_wrapper (
621 EmpathyDispatchOperation
*operation
)
623 EmpathyDispatchOperationPriv
*priv
;
625 g_return_val_if_fail (EMPATHY_IS_DISPATCH_OPERATION (operation
), NULL
);
627 priv
= GET_PRIV (operation
);
629 return priv
->channel_wrapper
;
633 empathy_dispatch_operation_get_channel_type (
634 EmpathyDispatchOperation
*operation
)
636 EmpathyDispatchOperationPriv
*priv
;
638 g_return_val_if_fail (EMPATHY_IS_DISPATCH_OPERATION (operation
), NULL
);
640 priv
= GET_PRIV (operation
);
642 return tp_channel_get_channel_type (priv
->channel
);
646 empathy_dispatch_operation_get_channel_type_id (
647 EmpathyDispatchOperation
*operation
)
649 EmpathyDispatchOperationPriv
*priv
;
651 g_return_val_if_fail (EMPATHY_IS_DISPATCH_OPERATION (operation
), 0);
653 priv
= GET_PRIV (operation
);
655 return tp_channel_get_channel_type_id (priv
->channel
);
659 empathy_dispatch_operation_get_object_path (
660 EmpathyDispatchOperation
*operation
)
662 EmpathyDispatchOperationPriv
*priv
;
664 g_return_val_if_fail (EMPATHY_IS_DISPATCH_OPERATION (operation
), NULL
);
666 priv
= GET_PRIV (operation
);
668 return tp_proxy_get_object_path (TP_PROXY (priv
->channel
));
671 EmpathyDispatchOperationState
672 empathy_dispatch_operation_get_status (EmpathyDispatchOperation
*operation
)
674 EmpathyDispatchOperationPriv
*priv
;
676 g_return_val_if_fail (EMPATHY_IS_DISPATCH_OPERATION (operation
),
677 EMPATHY_DISPATCHER_OPERATION_STATE_PREPARING
);
679 priv
= GET_PRIV (operation
);
685 empathy_dispatch_operation_is_incoming (EmpathyDispatchOperation
*operation
)
687 EmpathyDispatchOperationPriv
*priv
;
689 g_return_val_if_fail (EMPATHY_IS_DISPATCH_OPERATION (operation
), FALSE
);
691 priv
= GET_PRIV (operation
);
693 return priv
->incoming
;