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-chat.h>
28 #include <libempathy/empathy-tp-call.h>
29 #include <libempathy/empathy-tp-file.h>
31 #include "empathy-marshal.h"
33 #include "extensions/extensions.h"
35 #define DEBUG_FLAG EMPATHY_DEBUG_DISPATCHER
36 #include <libempathy/empathy-debug.h>
38 G_DEFINE_TYPE(EmpathyDispatchOperation, empathy_dispatch_operation,
41 static void empathy_dispatch_operation_set_status (
42 EmpathyDispatchOperation *self, EmpathyDispatchOperationState status);
43 static void empathy_dispatch_operation_channel_ready_cb (TpChannel *channel,
44 const GError *error, gpointer user_data);
49 /* Ready for dispatching */
51 /* Approved by an approver, can only happens on incoming operations */
53 /* Claimed by a handler */
55 /* Error, channel went away, inspecting it failed etc */
60 static guint signals[LAST_SIGNAL] = {0};
72 /* private structure */
73 typedef struct _EmpathyDispatchOperationPriv \
74 EmpathyDispatchOperationPriv;
76 struct _EmpathyDispatchOperationPriv
78 gboolean dispose_has_run;
79 TpConnection *connection;
81 GObject *channel_wrapper;
82 EmpathyContact *contact;
83 EmpathyDispatchOperationState status;
86 gulong invalidated_handler;
91 (G_TYPE_INSTANCE_GET_PRIVATE ((o), EMPATHY_TYPE_DISPATCH_OPERATION, \
92 EmpathyDispatchOperationPriv))
95 empathy_dispatch_operation_init (EmpathyDispatchOperation *obj)
97 //EmpathyDispatchOperationPriv *priv =
100 /* allocate any data required by the object here */
103 static void empathy_dispatch_operation_dispose (GObject *object);
104 static void empathy_dispatch_operation_finalize (GObject *object);
107 empathy_dispatch_operation_set_property (GObject *object,
108 guint property_id, const GValue *value, GParamSpec *pspec)
110 EmpathyDispatchOperation *operation = EMPATHY_DISPATCH_OPERATION (object);
111 EmpathyDispatchOperationPriv *priv = GET_PRIV (operation);
115 case PROP_CONNECTION:
116 priv->connection = g_value_dup_object (value);
119 priv->channel = g_value_dup_object (value);
121 case PROP_CHANNEL_WRAPPER:
122 priv->channel_wrapper = g_value_dup_object (value);
125 if (priv->contact != NULL)
126 g_object_unref (priv->contact);
127 priv->contact = g_value_dup_object (value);
130 priv->incoming = g_value_get_boolean (value);
136 empathy_dispatch_operation_get_property (GObject *object,
137 guint property_id, GValue *value, GParamSpec *pspec)
139 EmpathyDispatchOperation *operation = EMPATHY_DISPATCH_OPERATION (object);
140 EmpathyDispatchOperationPriv *priv = GET_PRIV (operation);
144 case PROP_CONNECTION:
145 g_value_set_object (value, priv->connection);
148 g_value_set_object (value, priv->channel);
150 case PROP_CHANNEL_WRAPPER:
151 g_value_set_object (value, priv->channel_wrapper);
154 g_value_set_object (value, priv->contact);
157 g_value_set_boolean (value, priv->incoming);
160 g_value_set_enum (value, priv->status);
166 empathy_dispatch_operation_invalidated (TpProxy *proxy, guint domain,
167 gint code, char *message, EmpathyDispatchOperation *self)
169 empathy_dispatch_operation_set_status (self,
170 EMPATHY_DISPATCHER_OPERATION_STATE_INVALIDATED);
172 g_signal_emit (self, signals[INVALIDATED], 0, domain, code, message);
176 empathy_dispatch_operation_constructed (GObject *object)
178 EmpathyDispatchOperation *self = EMPATHY_DISPATCH_OPERATION (object);
179 EmpathyDispatchOperationPriv *priv = GET_PRIV (self);
181 empathy_dispatch_operation_set_status (self,
182 EMPATHY_DISPATCHER_OPERATION_STATE_PREPARING);
184 priv->invalidated_handler =
185 g_signal_connect (priv->channel, "invalidated",
186 G_CALLBACK (empathy_dispatch_operation_invalidated), self);
188 tp_channel_call_when_ready (priv->channel,
189 empathy_dispatch_operation_channel_ready_cb, self);
193 empathy_dispatch_operation_class_init (
194 EmpathyDispatchOperationClass *empathy_dispatch_operation_class)
196 GObjectClass *object_class =
197 G_OBJECT_CLASS (empathy_dispatch_operation_class);
198 GParamSpec *param_spec;
200 g_type_class_add_private (empathy_dispatch_operation_class,
201 sizeof (EmpathyDispatchOperationPriv));
203 object_class->set_property = empathy_dispatch_operation_set_property;
204 object_class->get_property = empathy_dispatch_operation_get_property;
206 object_class->dispose = empathy_dispatch_operation_dispose;
207 object_class->finalize = empathy_dispatch_operation_finalize;
208 object_class->constructed = empathy_dispatch_operation_constructed;
210 signals[READY] = g_signal_new ("ready",
211 G_OBJECT_CLASS_TYPE(empathy_dispatch_operation_class),
215 g_cclosure_marshal_VOID__VOID,
218 signals[APPROVED] = g_signal_new ("approved",
219 G_OBJECT_CLASS_TYPE(empathy_dispatch_operation_class),
223 g_cclosure_marshal_VOID__VOID,
226 signals[CLAIMED] = g_signal_new ("claimed",
227 G_OBJECT_CLASS_TYPE(empathy_dispatch_operation_class),
231 g_cclosure_marshal_VOID__VOID,
234 signals[INVALIDATED] = g_signal_new ("invalidated",
235 G_OBJECT_CLASS_TYPE(empathy_dispatch_operation_class),
239 _empathy_marshal_VOID__UINT_INT_STRING,
240 G_TYPE_NONE, 3, G_TYPE_UINT, G_TYPE_INT, G_TYPE_STRING);
242 param_spec = g_param_spec_object ("connection",
243 "connection", "The telepathy connection",
245 G_PARAM_CONSTRUCT_ONLY |
246 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
247 g_object_class_install_property (object_class, PROP_CONNECTION,
250 param_spec = g_param_spec_object ("channel",
251 "channel", "The telepathy channel",
253 G_PARAM_CONSTRUCT_ONLY |
254 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
255 g_object_class_install_property (object_class, PROP_CHANNEL,
258 param_spec = g_param_spec_object ("channel-wrapper",
259 "channel wrapper", "The empathy specific channel wrapper",
261 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
262 g_object_class_install_property (object_class, PROP_CHANNEL_WRAPPER,
265 param_spec = g_param_spec_object ("contact",
266 "contact", "The empathy contact",
267 EMPATHY_TYPE_CONTACT,
268 G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
269 g_object_class_install_property (object_class, PROP_CONTACT,
272 param_spec = g_param_spec_boolean ("incoming",
273 "incoming", "Whether or not the channel is incoming",
275 G_PARAM_CONSTRUCT_ONLY |
276 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
277 g_object_class_install_property (object_class, PROP_INCOMING,
280 param_spec = g_param_spec_enum ("status",
281 "status", "Status of the dispatch operation",
282 EMPATHY_TYPE_DISPATCH_OPERATION_STATE, 0,
283 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
284 g_object_class_install_property (object_class, PROP_STATUS, param_spec);
288 empathy_dispatch_operation_dispose (GObject *object)
290 EmpathyDispatchOperation *self = EMPATHY_DISPATCH_OPERATION (object);
291 EmpathyDispatchOperationPriv *priv =
294 if (priv->dispose_has_run)
297 priv->dispose_has_run = TRUE;
299 g_object_unref (priv->connection);
301 if (priv->channel_wrapper != NULL)
302 g_object_unref (priv->channel_wrapper);
304 if (priv->ready_handler != 0)
305 g_signal_handler_disconnect (priv->channel_wrapper,
306 priv->invalidated_handler);
309 g_signal_handler_disconnect (priv->channel, priv->invalidated_handler);
310 g_object_unref (priv->channel);
313 if (priv->contact != NULL)
314 g_object_unref (priv->contact);
316 if (G_OBJECT_CLASS (empathy_dispatch_operation_parent_class)->dispose)
317 G_OBJECT_CLASS (empathy_dispatch_operation_parent_class)->dispose (object);
321 empathy_dispatch_operation_finalize (GObject *object)
323 /* free any data held directly by the object here */
324 G_OBJECT_CLASS (empathy_dispatch_operation_parent_class)->finalize (object);
328 empathy_dispatch_operation_set_status (EmpathyDispatchOperation *self,
329 EmpathyDispatchOperationState status)
331 EmpathyDispatchOperationPriv *priv = GET_PRIV (self);
333 g_assert (status >= priv->status);
336 if (priv->status != status)
338 DEBUG ("Dispatch operation %s status: %d -> %d",
339 empathy_dispatch_operation_get_object_path (self),
340 priv->status, status);
342 priv->status = status;
343 g_object_notify (G_OBJECT (self), "status");
345 if (status == EMPATHY_DISPATCHER_OPERATION_STATE_PENDING)
346 g_signal_emit (self, signals[READY], 0);
351 empathy_dispatcher_operation_tp_chat_ready_cb (GObject *object,
352 GParamSpec *spec, gpointer user_data)
354 EmpathyDispatchOperation *self = EMPATHY_DISPATCH_OPERATION (user_data);
355 EmpathyDispatchOperationPriv *priv = GET_PRIV (self);
357 if (!empathy_tp_chat_is_ready (EMPATHY_TP_CHAT (priv->channel_wrapper)))
360 g_signal_handler_disconnect (priv->channel_wrapper, priv->ready_handler);
361 priv->ready_handler = 0;
363 empathy_dispatch_operation_set_status (self,
364 EMPATHY_DISPATCHER_OPERATION_STATE_PENDING);
368 empathy_dispatch_operation_channel_ready_cb (TpChannel *channel,
369 const GError *error, gpointer user_data)
371 EmpathyDispatchOperation *self = EMPATHY_DISPATCH_OPERATION (user_data);
372 EmpathyDispatchOperationPriv *priv = GET_PRIV (self);
375 /* The error will be handled in empathy_dispatch_operation_invalidated */
379 g_assert (channel == priv->channel);
381 /* If the channel wrapper is defined, we assume it's ready */
382 if (priv->channel_wrapper != NULL)
385 channel_type = tp_channel_get_channel_type_id (channel);
387 if (channel_type == TP_IFACE_QUARK_CHANNEL_TYPE_TEXT)
389 EmpathyTpChat *chat= empathy_tp_chat_new (channel);
390 priv->channel_wrapper = G_OBJECT (chat);
392 if (!empathy_tp_chat_is_ready (chat))
394 priv->ready_handler = g_signal_connect (chat, "notify::ready",
395 G_CALLBACK (empathy_dispatcher_operation_tp_chat_ready_cb), self);
400 else if (channel_type == TP_IFACE_QUARK_CHANNEL_TYPE_STREAMED_MEDIA)
402 EmpathyTpCall *call = empathy_tp_call_new (channel);
403 priv->channel_wrapper = G_OBJECT (call);
406 else if (channel_type == TP_IFACE_QUARK_CHANNEL_TYPE_FILE_TRANSFER)
408 EmpathyTpFile *file = empathy_tp_file_new (channel);
409 priv->channel_wrapper = G_OBJECT (file);
413 empathy_dispatch_operation_set_status (self,
414 EMPATHY_DISPATCHER_OPERATION_STATE_PENDING);
417 EmpathyDispatchOperation *
418 empathy_dispatch_operation_new (TpConnection *connection, TpChannel *channel,
419 EmpathyContact *contact, gboolean incoming)
421 return empathy_dispatch_operation_new_with_wrapper (connection, channel,
422 contact, incoming, NULL);
425 EmpathyDispatchOperation *
426 empathy_dispatch_operation_new_with_wrapper (TpConnection *connection,
427 TpChannel *channel, EmpathyContact *contact, gboolean incoming,
430 g_return_val_if_fail (connection != NULL, NULL);
431 g_return_val_if_fail (channel != NULL, NULL);
433 return EMPATHY_DISPATCH_OPERATION (
434 g_object_new (EMPATHY_TYPE_DISPATCH_OPERATION,
435 "connection", connection,
437 "channel-wrapper", wrapper,
439 "incoming", incoming,
444 empathy_dispatch_operation_start (EmpathyDispatchOperation *operation)
446 EmpathyDispatchOperationPriv *priv;
448 g_return_if_fail (EMPATHY_IS_DISPATCH_OPERATION (operation));
450 priv = GET_PRIV (operation);
453 priv->status == EMPATHY_DISPATCHER_OPERATION_STATE_PENDING);
455 if (priv->incoming && !priv->approved)
456 empathy_dispatch_operation_set_status (operation,
457 EMPATHY_DISPATCHER_OPERATION_STATE_APPROVING);
459 empathy_dispatch_operation_set_status (operation,
460 EMPATHY_DISPATCHER_OPERATION_STATE_DISPATCHING);
464 empathy_dispatch_operation_approve (EmpathyDispatchOperation *operation)
466 EmpathyDispatchOperationPriv *priv;
468 g_return_if_fail (EMPATHY_IS_DISPATCH_OPERATION (operation));
470 priv = GET_PRIV (operation);
472 if (priv->status == EMPATHY_DISPATCHER_OPERATION_STATE_APPROVING)
474 DEBUG ("Approving operation %s",
475 empathy_dispatch_operation_get_object_path (operation));
477 empathy_dispatch_operation_set_status (operation,
478 EMPATHY_DISPATCHER_OPERATION_STATE_DISPATCHING);
480 g_signal_emit (operation, signals[APPROVED], 0);
482 else if (priv->status < EMPATHY_DISPATCHER_OPERATION_STATE_APPROVING)
484 DEBUG ("Pre-approving operation %s",
485 empathy_dispatch_operation_get_object_path (operation));
486 priv->approved = TRUE;
491 "Ignoring approval for %s as it's already past the approval stage",
492 empathy_dispatch_operation_get_object_path (operation));
496 /* Returns whether or not the operation was successfully claimed */
498 empathy_dispatch_operation_claim (EmpathyDispatchOperation *operation)
500 EmpathyDispatchOperationPriv *priv;
502 g_return_val_if_fail (EMPATHY_IS_DISPATCH_OPERATION (operation), FALSE);
504 priv = GET_PRIV (operation);
506 if (priv->status == EMPATHY_DISPATCHER_OPERATION_STATE_CLAIMED)
509 empathy_dispatch_operation_set_status (operation,
510 EMPATHY_DISPATCHER_OPERATION_STATE_CLAIMED);
512 g_signal_emit (operation, signals[CLAIMED], 0);
518 empathy_dispatch_operation_get_tp_connection (
519 EmpathyDispatchOperation *operation)
521 EmpathyDispatchOperationPriv *priv;
523 g_return_val_if_fail (EMPATHY_IS_DISPATCH_OPERATION (operation), NULL);
525 priv = GET_PRIV (operation);
527 return g_object_ref (priv->connection);
531 empathy_dispatch_operation_get_channel (EmpathyDispatchOperation *operation)
533 EmpathyDispatchOperationPriv *priv;
535 g_return_val_if_fail (EMPATHY_IS_DISPATCH_OPERATION (operation), NULL);
537 priv = GET_PRIV (operation);
539 return priv->channel;
543 empathy_dispatch_operation_get_channel_wrapper (
544 EmpathyDispatchOperation *operation)
546 EmpathyDispatchOperationPriv *priv;
548 g_return_val_if_fail (EMPATHY_IS_DISPATCH_OPERATION (operation), NULL);
550 priv = GET_PRIV (operation);
552 return priv->channel_wrapper;
556 empathy_dispatch_operation_get_channel_type (
557 EmpathyDispatchOperation *operation)
559 EmpathyDispatchOperationPriv *priv;
561 g_return_val_if_fail (EMPATHY_IS_DISPATCH_OPERATION (operation), NULL);
563 priv = GET_PRIV (operation);
565 return tp_channel_get_channel_type (priv->channel);
569 empathy_dispatch_operation_get_channel_type_id (
570 EmpathyDispatchOperation *operation)
572 EmpathyDispatchOperationPriv *priv;
574 g_return_val_if_fail (EMPATHY_IS_DISPATCH_OPERATION (operation), 0);
576 priv = GET_PRIV (operation);
578 return tp_channel_get_channel_type_id (priv->channel);
582 empathy_dispatch_operation_get_object_path (
583 EmpathyDispatchOperation *operation)
585 EmpathyDispatchOperationPriv *priv;
587 g_return_val_if_fail (EMPATHY_IS_DISPATCH_OPERATION (operation), NULL);
589 priv = GET_PRIV (operation);
591 return tp_proxy_get_object_path (TP_PROXY (priv->channel));
594 EmpathyDispatchOperationState
595 empathy_dispatch_operation_get_status (EmpathyDispatchOperation *operation)
597 EmpathyDispatchOperationPriv *priv;
599 g_return_val_if_fail (EMPATHY_IS_DISPATCH_OPERATION (operation),
600 EMPATHY_DISPATCHER_OPERATION_STATE_PREPARING);
602 priv = GET_PRIV (operation);
608 empathy_dispatch_operation_is_incoming (EmpathyDispatchOperation *operation)
610 EmpathyDispatchOperationPriv *priv;
612 g_return_val_if_fail (EMPATHY_IS_DISPATCH_OPERATION (operation), FALSE);
614 priv = GET_PRIV (operation);
616 return priv->incoming;