]> git.0d.be Git - empathy.git/blob - libempathy/empathy-dispatch-operation.c
Port libempathy to EmpathyAccount
[empathy.git] / libempathy / empathy-dispatch-operation.c
1 /*
2  * empathy-dispatch-operation.c - Source for EmpathyDispatchOperation
3  * Copyright (C) 2008 Collabora Ltd.
4  * @author Sjoerd Simons <sjoerd.simons@collabora.co.uk>
5  *
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.
10  *
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.
15  *
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
19  */
20
21
22 #include <stdio.h>
23 #include <stdlib.h>
24
25 #include <telepathy-glib/interfaces.h>
26
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>
33
34 #include "empathy-marshal.h"
35
36 #include "extensions/extensions.h"
37
38 #define DEBUG_FLAG EMPATHY_DEBUG_DISPATCHER
39 #include <libempathy/empathy-debug.h>
40
41 G_DEFINE_TYPE(EmpathyDispatchOperation, empathy_dispatch_operation,
42   G_TYPE_OBJECT)
43
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);
48
49 /* signal enum */
50 enum
51 {
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
61 };
62
63 static guint signals[LAST_SIGNAL] = {0};
64
65 /* properties */
66 enum {
67   PROP_CONNECTION = 1,
68   PROP_CHANNEL,
69   PROP_CHANNEL_WRAPPER,
70   PROP_CONTACT,
71   PROP_INCOMING,
72   PROP_STATUS,
73 };
74
75 /* private structure */
76 typedef struct _EmpathyDispatchOperationPriv \
77   EmpathyDispatchOperationPriv;
78
79 struct _EmpathyDispatchOperationPriv
80 {
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;
91 };
92
93 #define GET_PRIV(o)  \
94   (G_TYPE_INSTANCE_GET_PRIVATE ((o), EMPATHY_TYPE_DISPATCH_OPERATION, \
95     EmpathyDispatchOperationPriv))
96
97 static void
98 empathy_dispatch_operation_init (EmpathyDispatchOperation *obj)
99 {
100   //EmpathyDispatchOperationPriv *priv =
101   //  GET_PRIV (obj);
102
103   /* allocate any data required by the object here */
104 }
105
106 static void empathy_dispatch_operation_dispose (GObject *object);
107 static void empathy_dispatch_operation_finalize (GObject *object);
108
109 static void
110 empathy_dispatch_operation_set_property (GObject *object,
111   guint property_id, const GValue *value, GParamSpec *pspec)
112 {
113   EmpathyDispatchOperation *operation = EMPATHY_DISPATCH_OPERATION (object);
114   EmpathyDispatchOperationPriv *priv = GET_PRIV (operation);
115
116   switch (property_id)
117     {
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;
135     }
136 }
137
138 static void
139 empathy_dispatch_operation_get_property (GObject *object,
140   guint property_id, GValue *value, GParamSpec *pspec)
141 {
142   EmpathyDispatchOperation *operation = EMPATHY_DISPATCH_OPERATION (object);
143   EmpathyDispatchOperationPriv *priv = GET_PRIV (operation);
144
145   switch (property_id)
146     {
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;
165     }
166 }
167
168 static void
169 empathy_dispatch_operation_invalidated (TpProxy *proxy, guint domain,
170   gint code, char *message, EmpathyDispatchOperation *self)
171 {
172   empathy_dispatch_operation_set_status (self,
173     EMPATHY_DISPATCHER_OPERATION_STATE_INVALIDATED);
174
175   g_signal_emit (self, signals[INVALIDATED], 0, domain, code, message);
176 }
177
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)
184 {
185   EmpathyDispatchOperationPriv *priv = GET_PRIV (self);
186
187   if (error)
188     {
189       /* FIXME: We should cancel the operation */
190       DEBUG ("Error: %s", error->message);
191       return;
192     }
193
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");
198
199   tp_channel_call_when_ready (priv->channel,
200     empathy_dispatch_operation_channel_ready_cb, self);
201 }
202
203 static void
204 empathy_dispatch_operation_constructed (GObject *object)
205 {
206   EmpathyDispatchOperation *self = EMPATHY_DISPATCH_OPERATION (object);
207   EmpathyDispatchOperationPriv *priv = GET_PRIV (self);
208   TpHandle handle;
209   TpHandleType handle_type;
210
211   empathy_dispatch_operation_set_status (self,
212     EMPATHY_DISPATCHER_OPERATION_STATE_PREPARING);
213
214   priv->invalidated_handler =
215     g_signal_connect (priv->channel, "invalidated",
216       G_CALLBACK (empathy_dispatch_operation_invalidated), self);
217
218   handle = tp_channel_get_handle (priv->channel, &handle_type);
219
220   if (handle_type == TP_HANDLE_TYPE_CONTACT && priv->contact == NULL)
221     {
222       EmpathyTpContactFactory *factory;
223
224       factory = empathy_tp_contact_factory_dup_singleton (priv->connection);
225       empathy_tp_contact_factory_get_from_handle (factory, handle,
226         dispatcher_operation_got_contact_cb, NULL, NULL, object);
227       g_object_unref (factory);
228       return;
229     }
230
231   tp_channel_call_when_ready (priv->channel,
232     empathy_dispatch_operation_channel_ready_cb, self);
233 }
234
235 static void
236 empathy_dispatch_operation_class_init (
237   EmpathyDispatchOperationClass *empathy_dispatch_operation_class)
238 {
239   GObjectClass *object_class =
240     G_OBJECT_CLASS (empathy_dispatch_operation_class);
241   GParamSpec *param_spec;
242
243   g_type_class_add_private (empathy_dispatch_operation_class,
244     sizeof (EmpathyDispatchOperationPriv));
245
246   object_class->set_property = empathy_dispatch_operation_set_property;
247   object_class->get_property = empathy_dispatch_operation_get_property;
248
249   object_class->dispose = empathy_dispatch_operation_dispose;
250   object_class->finalize = empathy_dispatch_operation_finalize;
251   object_class->constructed = empathy_dispatch_operation_constructed;
252
253   signals[READY] = g_signal_new ("ready",
254     G_OBJECT_CLASS_TYPE(empathy_dispatch_operation_class),
255       G_SIGNAL_RUN_LAST,
256       0,
257       NULL, NULL,
258       g_cclosure_marshal_VOID__VOID,
259       G_TYPE_NONE, 0);
260
261   signals[APPROVED] = g_signal_new ("approved",
262     G_OBJECT_CLASS_TYPE(empathy_dispatch_operation_class),
263       G_SIGNAL_RUN_LAST,
264       0,
265       NULL, NULL,
266       g_cclosure_marshal_VOID__VOID,
267       G_TYPE_NONE, 0);
268
269   signals[CLAIMED] = g_signal_new ("claimed",
270     G_OBJECT_CLASS_TYPE(empathy_dispatch_operation_class),
271       G_SIGNAL_RUN_LAST,
272       0,
273       NULL, NULL,
274       g_cclosure_marshal_VOID__VOID,
275       G_TYPE_NONE, 0);
276
277   signals[INVALIDATED] = g_signal_new ("invalidated",
278     G_OBJECT_CLASS_TYPE(empathy_dispatch_operation_class),
279       G_SIGNAL_RUN_LAST,
280       0,
281       NULL, NULL,
282       _empathy_marshal_VOID__UINT_INT_STRING,
283       G_TYPE_NONE, 3, G_TYPE_UINT, G_TYPE_INT, G_TYPE_STRING);
284
285   param_spec = g_param_spec_object ("connection",
286     "connection", "The telepathy connection",
287     TP_TYPE_CONNECTION,
288     G_PARAM_CONSTRUCT_ONLY |
289     G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
290   g_object_class_install_property (object_class, PROP_CONNECTION,
291                                   param_spec);
292
293   param_spec = g_param_spec_object ("channel",
294     "channel", "The telepathy channel",
295     TP_TYPE_CHANNEL,
296     G_PARAM_CONSTRUCT_ONLY |
297     G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
298   g_object_class_install_property (object_class, PROP_CHANNEL,
299                                   param_spec);
300
301   param_spec = g_param_spec_object ("channel-wrapper",
302     "channel wrapper", "The empathy specific channel wrapper",
303     G_TYPE_OBJECT,
304     G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
305   g_object_class_install_property (object_class, PROP_CHANNEL_WRAPPER,
306                                   param_spec);
307
308   param_spec = g_param_spec_object ("contact",
309     "contact", "The empathy contact",
310     EMPATHY_TYPE_CONTACT,
311     G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
312   g_object_class_install_property (object_class, PROP_CONTACT,
313                                   param_spec);
314
315   param_spec = g_param_spec_boolean ("incoming",
316     "incoming", "Whether or not the channel is incoming",
317     FALSE,
318     G_PARAM_CONSTRUCT_ONLY |
319     G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
320   g_object_class_install_property (object_class, PROP_INCOMING,
321                                   param_spec);
322
323   param_spec = g_param_spec_enum ("status",
324     "status", "Status of the dispatch operation",
325     EMPATHY_TYPE_DISPATCH_OPERATION_STATE, 0,
326     G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
327   g_object_class_install_property (object_class, PROP_STATUS, param_spec);
328 }
329
330 void
331 empathy_dispatch_operation_dispose (GObject *object)
332 {
333   EmpathyDispatchOperation *self = EMPATHY_DISPATCH_OPERATION (object);
334   EmpathyDispatchOperationPriv *priv =
335     GET_PRIV (self);
336
337   if (priv->dispose_has_run)
338     return;
339
340   priv->dispose_has_run = TRUE;
341
342   g_object_unref (priv->connection);
343
344   if (priv->channel_wrapper != NULL)
345     g_object_unref (priv->channel_wrapper);
346
347   if (priv->ready_handler != 0)
348     g_signal_handler_disconnect (priv->channel_wrapper,
349       priv->invalidated_handler);
350
351
352   g_signal_handler_disconnect (priv->channel, priv->invalidated_handler);
353   g_object_unref (priv->channel);
354
355
356   if (priv->contact != NULL)
357     g_object_unref (priv->contact);
358
359   if (G_OBJECT_CLASS (empathy_dispatch_operation_parent_class)->dispose)
360     G_OBJECT_CLASS (empathy_dispatch_operation_parent_class)->dispose (object);
361 }
362
363 void
364 empathy_dispatch_operation_finalize (GObject *object)
365 {
366   /* free any data held directly by the object here */
367   G_OBJECT_CLASS (empathy_dispatch_operation_parent_class)->finalize (object);
368 }
369
370 static void
371 empathy_dispatch_operation_set_status (EmpathyDispatchOperation *self,
372   EmpathyDispatchOperationState status)
373 {
374   EmpathyDispatchOperationPriv *priv = GET_PRIV (self);
375
376   g_assert (status >= priv->status);
377
378
379   if (priv->status != status)
380     {
381       DEBUG ("Dispatch operation %s status: %d -> %d",
382         empathy_dispatch_operation_get_object_path (self),
383         priv->status, status);
384
385       priv->status = status;
386       g_object_notify (G_OBJECT (self), "status");
387
388       if (status == EMPATHY_DISPATCHER_OPERATION_STATE_PENDING)
389         g_signal_emit (self, signals[READY], 0);
390     }
391 }
392
393 static void
394 empathy_dispatcher_operation_tp_chat_ready_cb (GObject *object,
395   GParamSpec *spec, gpointer user_data)
396 {
397   EmpathyDispatchOperation *self = EMPATHY_DISPATCH_OPERATION (user_data);
398   EmpathyDispatchOperationPriv *priv = GET_PRIV (self);
399
400   if (!empathy_tp_chat_is_ready (EMPATHY_TP_CHAT (priv->channel_wrapper)))
401     return;
402
403   g_signal_handler_disconnect (priv->channel_wrapper, priv->ready_handler);
404   priv->ready_handler = 0;
405
406   empathy_dispatch_operation_set_status (self,
407     EMPATHY_DISPATCHER_OPERATION_STATE_PENDING);
408 }
409
410 static void
411 empathy_dispatch_operation_channel_ready_cb (TpChannel *channel,
412   const GError *error, gpointer user_data)
413 {
414   EmpathyDispatchOperation *self = EMPATHY_DISPATCH_OPERATION (user_data);
415   EmpathyDispatchOperationPriv *priv = GET_PRIV (self);
416   GQuark channel_type;
417
418   /* The error will be handled in empathy_dispatch_operation_invalidated */
419   if (error != NULL)
420     return;
421
422   g_assert (channel == priv->channel);
423
424   /* If the channel wrapper is defined, we assume it's ready */
425   if (priv->channel_wrapper != NULL)
426     goto ready;
427
428   channel_type = tp_channel_get_channel_type_id (channel);
429
430   if (channel_type == TP_IFACE_QUARK_CHANNEL_TYPE_TEXT)
431     {
432       EmpathyTpChat *chat= empathy_tp_chat_new (channel);
433       priv->channel_wrapper = G_OBJECT (chat);
434
435       if (!empathy_tp_chat_is_ready (chat))
436         {
437           priv->ready_handler = g_signal_connect (chat, "notify::ready",
438             G_CALLBACK (empathy_dispatcher_operation_tp_chat_ready_cb), self);
439           return;
440         }
441     }
442   else if (channel_type == TP_IFACE_QUARK_CHANNEL_TYPE_STREAMED_MEDIA)
443     {
444       EmpathyTpCall *call = empathy_tp_call_new (channel);
445       priv->channel_wrapper = G_OBJECT (call);
446     }
447   else if (channel_type == TP_IFACE_QUARK_CHANNEL_TYPE_FILE_TRANSFER)
448     {
449        EmpathyTpFile *file = empathy_tp_file_new (channel, priv->incoming);
450        priv->channel_wrapper = G_OBJECT (file);
451     }
452
453 ready:
454   empathy_dispatch_operation_set_status (self,
455     EMPATHY_DISPATCHER_OPERATION_STATE_PENDING);
456 }
457
458 EmpathyDispatchOperation *
459 empathy_dispatch_operation_new (TpConnection *connection, TpChannel *channel,
460   EmpathyContact *contact, gboolean incoming)
461 {
462   return empathy_dispatch_operation_new_with_wrapper (connection, channel,
463     contact, incoming, NULL);
464 }
465
466 EmpathyDispatchOperation *
467 empathy_dispatch_operation_new_with_wrapper (TpConnection *connection,
468   TpChannel *channel, EmpathyContact *contact, gboolean incoming,
469   GObject *wrapper)
470 {
471   g_return_val_if_fail (connection != NULL, NULL);
472   g_return_val_if_fail (channel != NULL, NULL);
473
474   return EMPATHY_DISPATCH_OPERATION (
475     g_object_new (EMPATHY_TYPE_DISPATCH_OPERATION,
476       "connection", connection,
477       "channel", channel,
478       "channel-wrapper", wrapper,
479       "contact", contact,
480       "incoming", incoming,
481       NULL));
482 }
483
484 void
485 empathy_dispatch_operation_start (EmpathyDispatchOperation *operation)
486 {
487   EmpathyDispatchOperationPriv *priv;
488
489   g_return_if_fail (EMPATHY_IS_DISPATCH_OPERATION (operation));
490
491   priv = GET_PRIV (operation);
492
493   g_return_if_fail (
494     priv->status == EMPATHY_DISPATCHER_OPERATION_STATE_PENDING);
495
496   if (priv->incoming && !priv->approved)
497     empathy_dispatch_operation_set_status (operation,
498       EMPATHY_DISPATCHER_OPERATION_STATE_APPROVING);
499   else
500     empathy_dispatch_operation_set_status (operation,
501       EMPATHY_DISPATCHER_OPERATION_STATE_DISPATCHING);
502 }
503
504 void
505 empathy_dispatch_operation_approve (EmpathyDispatchOperation *operation)
506 {
507   EmpathyDispatchOperationPriv *priv;
508
509   g_return_if_fail (EMPATHY_IS_DISPATCH_OPERATION (operation));
510
511   priv = GET_PRIV (operation);
512
513   if (priv->status == EMPATHY_DISPATCHER_OPERATION_STATE_APPROVING)
514     {
515       DEBUG ("Approving operation %s",
516         empathy_dispatch_operation_get_object_path (operation));
517
518       empathy_dispatch_operation_set_status (operation,
519         EMPATHY_DISPATCHER_OPERATION_STATE_DISPATCHING);
520
521       g_signal_emit (operation, signals[APPROVED], 0);
522     }
523   else if (priv->status < EMPATHY_DISPATCHER_OPERATION_STATE_APPROVING)
524     {
525       DEBUG ("Pre-approving operation %s",
526         empathy_dispatch_operation_get_object_path (operation));
527       priv->approved = TRUE;
528     }
529   else
530     {
531       DEBUG (
532         "Ignoring approval for %s as it's already past the approval stage",
533         empathy_dispatch_operation_get_object_path (operation));
534     }
535 }
536
537 /* Returns whether or not the operation was successfully claimed */
538 gboolean
539 empathy_dispatch_operation_claim (EmpathyDispatchOperation *operation)
540 {
541   EmpathyDispatchOperationPriv *priv;
542
543   g_return_val_if_fail (EMPATHY_IS_DISPATCH_OPERATION (operation), FALSE);
544
545   priv = GET_PRIV (operation);
546
547   if (priv->status == EMPATHY_DISPATCHER_OPERATION_STATE_CLAIMED)
548     return FALSE;
549
550   empathy_dispatch_operation_set_status (operation,
551     EMPATHY_DISPATCHER_OPERATION_STATE_CLAIMED);
552
553   g_signal_emit (operation, signals[CLAIMED], 0);
554
555   return TRUE;
556 }
557
558 TpConnection *
559 empathy_dispatch_operation_get_tp_connection (
560   EmpathyDispatchOperation *operation)
561 {
562   EmpathyDispatchOperationPriv *priv;
563
564   g_return_val_if_fail (EMPATHY_IS_DISPATCH_OPERATION (operation), NULL);
565
566   priv = GET_PRIV (operation);
567
568   return priv->connection;
569 }
570
571 TpChannel *
572 empathy_dispatch_operation_get_channel (EmpathyDispatchOperation *operation)
573 {
574   EmpathyDispatchOperationPriv *priv;
575
576   g_return_val_if_fail (EMPATHY_IS_DISPATCH_OPERATION (operation), NULL);
577
578   priv = GET_PRIV (operation);
579
580   return priv->channel;
581 }
582
583 GObject *
584 empathy_dispatch_operation_get_channel_wrapper (
585   EmpathyDispatchOperation *operation)
586 {
587   EmpathyDispatchOperationPriv *priv;
588
589   g_return_val_if_fail (EMPATHY_IS_DISPATCH_OPERATION (operation), NULL);
590
591   priv = GET_PRIV (operation);
592
593   return priv->channel_wrapper;
594 }
595
596 const gchar *
597 empathy_dispatch_operation_get_channel_type (
598   EmpathyDispatchOperation *operation)
599 {
600   EmpathyDispatchOperationPriv *priv;
601
602   g_return_val_if_fail (EMPATHY_IS_DISPATCH_OPERATION (operation), NULL);
603
604   priv = GET_PRIV (operation);
605
606   return tp_channel_get_channel_type (priv->channel);
607 }
608
609 GQuark
610 empathy_dispatch_operation_get_channel_type_id (
611   EmpathyDispatchOperation *operation)
612 {
613   EmpathyDispatchOperationPriv *priv;
614
615   g_return_val_if_fail (EMPATHY_IS_DISPATCH_OPERATION (operation), 0);
616
617   priv = GET_PRIV (operation);
618
619   return tp_channel_get_channel_type_id (priv->channel);
620 }
621
622 const gchar *
623 empathy_dispatch_operation_get_object_path (
624   EmpathyDispatchOperation *operation)
625 {
626   EmpathyDispatchOperationPriv *priv;
627
628   g_return_val_if_fail (EMPATHY_IS_DISPATCH_OPERATION (operation), NULL);
629
630   priv = GET_PRIV (operation);
631
632   return tp_proxy_get_object_path (TP_PROXY (priv->channel));
633 }
634
635 EmpathyDispatchOperationState
636 empathy_dispatch_operation_get_status (EmpathyDispatchOperation *operation)
637 {
638   EmpathyDispatchOperationPriv *priv;
639
640   g_return_val_if_fail (EMPATHY_IS_DISPATCH_OPERATION (operation),
641     EMPATHY_DISPATCHER_OPERATION_STATE_PREPARING);
642
643   priv = GET_PRIV (operation);
644
645   return priv->status;
646 }
647
648 gboolean
649 empathy_dispatch_operation_is_incoming (EmpathyDispatchOperation *operation)
650 {
651   EmpathyDispatchOperationPriv *priv;
652
653   g_return_val_if_fail (EMPATHY_IS_DISPATCH_OPERATION (operation), FALSE);
654
655   priv = GET_PRIV (operation);
656
657   return priv->incoming;
658 }