Improve default display name for Facebook accounts
[empathy-mirror.git] / libempathy / empathy-dispatch-operation.c
blob20ebadb83c611c81f5a6a9d83ea25afb91b92c4a
1 /*
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
22 #include <stdio.h>
23 #include <stdlib.h>
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,
42 G_TYPE_OBJECT)
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);
49 /* signal enum */
50 enum
52 /* Ready for dispatching */
53 READY,
54 /* Approved by an approver, can only happens on incoming operations */
55 APPROVED,
56 /* Claimed by a handler */
57 CLAIMED,
58 /* Error, channel went away, inspecting it failed etc */
59 INVALIDATED,
60 LAST_SIGNAL
63 static guint signals[LAST_SIGNAL] = {0};
65 /* properties */
66 enum {
67 PROP_CONNECTION = 1,
68 PROP_CHANNEL,
69 PROP_CHANNEL_WRAPPER,
70 PROP_CONTACT,
71 PROP_INCOMING,
72 PROP_STATUS,
75 /* private structure */
76 typedef struct _EmpathyDispatchOperationPriv \
77 EmpathyDispatchOperationPriv;
79 struct _EmpathyDispatchOperationPriv
81 gboolean dispose_has_run;
82 TpConnection *connection;
83 TpChannel *channel;
84 GObject *channel_wrapper;
85 EmpathyContact *contact;
86 EmpathyDispatchOperationState status;
87 gboolean incoming;
88 gboolean approved;
89 gulong invalidated_handler;
90 gulong ready_handler;
93 #define GET_PRIV(o) \
94 (G_TYPE_INSTANCE_GET_PRIVATE ((o), EMPATHY_TYPE_DISPATCH_OPERATION, \
95 EmpathyDispatchOperationPriv))
97 static void
98 empathy_dispatch_operation_init (EmpathyDispatchOperation *obj)
100 //EmpathyDispatchOperationPriv *priv =
101 // GET_PRIV (obj);
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);
109 static void
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);
116 switch (property_id)
118 case PROP_CONNECTION:
119 priv->connection = g_value_dup_object (value);
120 break;
121 case PROP_CHANNEL:
122 priv->channel = g_value_dup_object (value);
123 break;
124 case PROP_CHANNEL_WRAPPER:
125 priv->channel_wrapper = g_value_dup_object (value);
126 break;
127 case PROP_CONTACT:
128 if (priv->contact != NULL)
129 g_object_unref (priv->contact);
130 priv->contact = g_value_dup_object (value);
131 break;
132 case PROP_INCOMING:
133 priv->incoming = g_value_get_boolean (value);
134 break;
138 static void
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);
145 switch (property_id)
147 case PROP_CONNECTION:
148 g_value_set_object (value, priv->connection);
149 break;
150 case PROP_CHANNEL:
151 g_value_set_object (value, priv->channel);
152 break;
153 case PROP_CHANNEL_WRAPPER:
154 g_value_set_object (value, priv->channel_wrapper);
155 break;
156 case PROP_CONTACT:
157 g_value_set_object (value, priv->contact);
158 break;
159 case PROP_INCOMING:
160 g_value_set_boolean (value, priv->incoming);
161 break;
162 case PROP_STATUS:
163 g_value_set_enum (value, priv->status);
164 break;
168 static void
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);
178 static void
179 dispatcher_operation_got_contact_cb (EmpathyTpContactFactory *factory,
180 EmpathyContact *contact,
181 const GError *error,
182 gpointer user_data,
183 GObject *self)
185 EmpathyDispatchOperationPriv *priv = GET_PRIV (self);
187 if (error)
189 /* FIXME: We should cancel the operation */
190 DEBUG ("Error: %s", error->message);
191 return;
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
200 * running */
201 g_object_ref (self);
202 tp_channel_call_when_ready (priv->channel,
203 empathy_dispatch_operation_channel_ready_cb, self);
206 static void
207 dispatch_operation_connection_ready (TpConnection *connection,
208 const GError *error,
209 gpointer user_data)
211 EmpathyDispatchOperation *self = EMPATHY_DISPATCH_OPERATION (user_data);
212 EmpathyDispatchOperationPriv *priv = GET_PRIV (self);
213 EmpathyTpContactFactory *factory;
214 TpHandle handle;
216 if (error != NULL)
217 goto out;
219 if (priv->status >= EMPATHY_DISPATCHER_OPERATION_STATE_CLAIMED)
220 /* no point to get more information */
221 goto out;
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);
231 out:
232 g_object_unref (self);
235 static void
236 empathy_dispatch_operation_constructed (GObject *object)
238 EmpathyDispatchOperation *self = EMPATHY_DISPATCH_OPERATION (object);
239 EmpathyDispatchOperationPriv *priv = GET_PRIV (self);
240 TpHandle handle;
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
255 * running */
256 g_object_ref (self);
257 tp_connection_call_when_ready (priv->connection,
258 dispatch_operation_connection_ready, object);
259 return;
262 g_object_ref (self);
263 tp_channel_call_when_ready (priv->channel,
264 empathy_dispatch_operation_channel_ready_cb, self);
267 static void
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),
287 G_SIGNAL_RUN_LAST,
289 NULL, NULL,
290 g_cclosure_marshal_VOID__VOID,
291 G_TYPE_NONE, 0);
293 signals[APPROVED] = g_signal_new ("approved",
294 G_OBJECT_CLASS_TYPE(empathy_dispatch_operation_class),
295 G_SIGNAL_RUN_LAST,
297 NULL, NULL,
298 g_cclosure_marshal_VOID__VOID,
299 G_TYPE_NONE, 0);
301 signals[CLAIMED] = g_signal_new ("claimed",
302 G_OBJECT_CLASS_TYPE(empathy_dispatch_operation_class),
303 G_SIGNAL_RUN_LAST,
305 NULL, NULL,
306 g_cclosure_marshal_VOID__VOID,
307 G_TYPE_NONE, 0);
309 signals[INVALIDATED] = g_signal_new ("invalidated",
310 G_OBJECT_CLASS_TYPE(empathy_dispatch_operation_class),
311 G_SIGNAL_RUN_LAST,
313 NULL, NULL,
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",
319 TP_TYPE_CONNECTION,
320 G_PARAM_CONSTRUCT_ONLY |
321 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
322 g_object_class_install_property (object_class, PROP_CONNECTION,
323 param_spec);
325 param_spec = g_param_spec_object ("channel",
326 "channel", "The telepathy channel",
327 TP_TYPE_CHANNEL,
328 G_PARAM_CONSTRUCT_ONLY |
329 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
330 g_object_class_install_property (object_class, PROP_CHANNEL,
331 param_spec);
333 param_spec = g_param_spec_object ("channel-wrapper",
334 "channel wrapper", "The empathy specific channel wrapper",
335 G_TYPE_OBJECT,
336 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
337 g_object_class_install_property (object_class, PROP_CHANNEL_WRAPPER,
338 param_spec);
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,
345 param_spec);
347 param_spec = g_param_spec_boolean ("incoming",
348 "incoming", "Whether or not the channel is incoming",
349 FALSE,
350 G_PARAM_CONSTRUCT_ONLY |
351 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
352 g_object_class_install_property (object_class, PROP_INCOMING,
353 param_spec);
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);
362 void
363 empathy_dispatch_operation_dispose (GObject *object)
365 EmpathyDispatchOperation *self = EMPATHY_DISPATCH_OPERATION (object);
366 EmpathyDispatchOperationPriv *priv =
367 GET_PRIV (self);
369 if (priv->dispose_has_run)
370 return;
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);
393 void
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);
400 static void
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);
423 static void
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)))
431 return;
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);
440 static void
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);
446 GQuark channel_type;
448 /* The error will be handled in empathy_dispatch_operation_invalidated */
449 if (error != NULL)
450 goto out;
452 g_assert (channel == priv->channel);
454 if (priv->status >= EMPATHY_DISPATCHER_OPERATION_STATE_CLAIMED)
455 /* no point to get more information */
456 goto out;
458 /* If the channel wrapper is defined, we assume it's ready */
459 if (priv->channel_wrapper != NULL)
460 goto ready;
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);
473 goto out;
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);
487 ready:
488 empathy_dispatch_operation_set_status (self,
489 EMPATHY_DISPATCHER_OPERATION_STATE_PENDING);
490 out:
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,
505 GObject *wrapper)
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,
513 "channel", channel,
514 "channel-wrapper", wrapper,
515 "contact", contact,
516 "incoming", incoming,
517 NULL));
520 void
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);
529 g_return_if_fail (
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);
535 else
536 empathy_dispatch_operation_set_status (operation,
537 EMPATHY_DISPATCHER_OPERATION_STATE_DISPATCHING);
540 void
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;
565 else
567 DEBUG (
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 */
574 gboolean
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)
584 return FALSE;
586 empathy_dispatch_operation_set_status (operation,
587 EMPATHY_DISPATCHER_OPERATION_STATE_CLAIMED);
589 g_signal_emit (operation, signals[CLAIMED], 0);
591 return TRUE;
594 TpConnection *
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;
607 TpChannel *
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;
619 GObject *
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;
632 const gchar *
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);
645 GQuark
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);
658 const gchar *
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);
681 return priv->status;
684 gboolean
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;