]> git.0d.be Git - empathy.git/blob - libempathy/empathy-dispatch-operation.c
1786e4169039c37a110ea3116c26613ef4782a91
[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 "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>
30
31 #include "empathy-marshal.h"
32
33 #include "extensions/extensions.h"
34
35 #define DEBUG_FLAG EMPATHY_DEBUG_DISPATCHER
36 #include <libempathy/empathy-debug.h>
37
38 G_DEFINE_TYPE(EmpathyDispatchOperation, empathy_dispatch_operation,
39   G_TYPE_OBJECT)
40
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);
45
46 /* signal enum */
47 enum
48 {
49     /* Ready for dispatching */
50     READY,
51     /* Approved by an approver, can only happens on incoming operations */
52     APPROVED,
53     /* Claimed by a handler */
54     CLAIMED,
55     /* Error, channel went away, inspecting it failed etc */
56     INVALIDATED,
57     LAST_SIGNAL
58 };
59
60 static guint signals[LAST_SIGNAL] = {0};
61
62 /* properties */
63 enum {
64   PROP_CONNECTION = 1,
65   PROP_CHANNEL,
66   PROP_CHANNEL_WRAPPER,
67   PROP_CONTACT,
68   PROP_INCOMING,
69   PROP_STATUS,
70 };
71
72 /* private structure */
73 typedef struct _EmpathyDispatchOperationPriv \
74   EmpathyDispatchOperationPriv;
75
76 struct _EmpathyDispatchOperationPriv
77 {
78   gboolean dispose_has_run;
79   TpConnection *connection;
80   TpChannel *channel;
81   GObject *channel_wrapper;
82   EmpathyContact *contact;
83   EmpathyDispatchOperationState status;
84   gboolean incoming;
85   gboolean approved;
86   gulong invalidated_handler;
87   gulong ready_handler;
88 };
89
90 #define GET_PRIV(o)  \
91   (G_TYPE_INSTANCE_GET_PRIVATE ((o), EMPATHY_TYPE_DISPATCH_OPERATION, \
92     EmpathyDispatchOperationPriv))
93
94 static void
95 empathy_dispatch_operation_init (EmpathyDispatchOperation *obj)
96 {
97   //EmpathyDispatchOperationPriv *priv =
98   //  GET_PRIV (obj);
99
100   /* allocate any data required by the object here */
101 }
102
103 static void empathy_dispatch_operation_dispose (GObject *object);
104 static void empathy_dispatch_operation_finalize (GObject *object);
105
106 static void
107 empathy_dispatch_operation_set_property (GObject *object,
108   guint property_id, const GValue *value, GParamSpec *pspec)
109 {
110   EmpathyDispatchOperation *operation = EMPATHY_DISPATCH_OPERATION (object);
111   EmpathyDispatchOperationPriv *priv = GET_PRIV (operation);
112
113   switch (property_id)
114     {
115       case PROP_CONNECTION:
116         priv->connection = g_value_dup_object (value);
117         break;
118       case PROP_CHANNEL:
119         priv->channel = g_value_dup_object (value);
120         break;
121       case PROP_CHANNEL_WRAPPER:
122         priv->channel_wrapper = g_value_dup_object (value);
123         break;
124       case PROP_CONTACT:
125         if (priv->contact != NULL)
126           g_object_unref (priv->contact);
127         priv->contact = g_value_dup_object (value);
128         break;
129       case PROP_INCOMING:
130         priv->incoming = g_value_get_boolean (value);
131         break;
132     }
133 }
134
135 static void
136 empathy_dispatch_operation_get_property (GObject *object,
137   guint property_id, GValue *value, GParamSpec *pspec)
138 {
139   EmpathyDispatchOperation *operation = EMPATHY_DISPATCH_OPERATION (object);
140   EmpathyDispatchOperationPriv *priv = GET_PRIV (operation);
141
142   switch (property_id)
143     {
144       case PROP_CONNECTION:
145         g_value_set_object (value, priv->connection);
146         break;
147       case PROP_CHANNEL:
148         g_value_set_object (value, priv->channel);
149         break;
150       case PROP_CHANNEL_WRAPPER:
151         g_value_set_object (value, priv->channel_wrapper);
152         break;
153       case PROP_CONTACT:
154         g_value_set_object (value, priv->contact);
155         break;
156       case PROP_INCOMING:
157         g_value_set_boolean (value, priv->incoming);
158         break;
159       case PROP_STATUS:
160         g_value_set_enum (value, priv->status);
161         break;
162     }
163 }
164
165 static void
166 empathy_dispatch_operation_invalidated (TpProxy *proxy, guint domain,
167   gint code, char *message, EmpathyDispatchOperation *self)
168 {
169   empathy_dispatch_operation_set_status (self,
170     EMPATHY_DISPATCHER_OPERATION_STATE_INVALIDATED);
171
172   g_signal_emit (self, signals[INVALIDATED], 0, domain, code, message);
173 }
174
175 static void
176 empathy_dispatch_operation_constructed (GObject *object)
177 {
178   EmpathyDispatchOperation *self = EMPATHY_DISPATCH_OPERATION (object);
179   EmpathyDispatchOperationPriv *priv = GET_PRIV (self);
180
181   empathy_dispatch_operation_set_status (self,
182     EMPATHY_DISPATCHER_OPERATION_STATE_PREPARING);
183
184   priv->invalidated_handler =
185     g_signal_connect (priv->channel, "invalidated",
186       G_CALLBACK (empathy_dispatch_operation_invalidated), self);
187
188   tp_channel_call_when_ready (priv->channel,
189     empathy_dispatch_operation_channel_ready_cb, self);
190 }
191
192 static void
193 empathy_dispatch_operation_class_init (
194   EmpathyDispatchOperationClass *empathy_dispatch_operation_class)
195 {
196   GObjectClass *object_class =
197     G_OBJECT_CLASS (empathy_dispatch_operation_class);
198   GParamSpec *param_spec;
199
200   g_type_class_add_private (empathy_dispatch_operation_class,
201     sizeof (EmpathyDispatchOperationPriv));
202
203   object_class->set_property = empathy_dispatch_operation_set_property;
204   object_class->get_property = empathy_dispatch_operation_get_property;
205
206   object_class->dispose = empathy_dispatch_operation_dispose;
207   object_class->finalize = empathy_dispatch_operation_finalize;
208   object_class->constructed = empathy_dispatch_operation_constructed;
209
210   signals[READY] = g_signal_new ("ready",
211     G_OBJECT_CLASS_TYPE(empathy_dispatch_operation_class),
212       G_SIGNAL_RUN_LAST,
213       0,
214       NULL, NULL,
215       g_cclosure_marshal_VOID__VOID,
216       G_TYPE_NONE, 0);
217
218   signals[APPROVED] = g_signal_new ("approved",
219     G_OBJECT_CLASS_TYPE(empathy_dispatch_operation_class),
220       G_SIGNAL_RUN_LAST,
221       0,
222       NULL, NULL,
223       g_cclosure_marshal_VOID__VOID,
224       G_TYPE_NONE, 0);
225
226   signals[CLAIMED] = g_signal_new ("claimed",
227     G_OBJECT_CLASS_TYPE(empathy_dispatch_operation_class),
228       G_SIGNAL_RUN_LAST,
229       0,
230       NULL, NULL,
231       g_cclosure_marshal_VOID__VOID,
232       G_TYPE_NONE, 0);
233
234   signals[INVALIDATED] = g_signal_new ("invalidated",
235     G_OBJECT_CLASS_TYPE(empathy_dispatch_operation_class),
236       G_SIGNAL_RUN_LAST,
237       0,
238       NULL, NULL,
239       _empathy_marshal_VOID__UINT_INT_STRING,
240       G_TYPE_NONE, 3, G_TYPE_UINT, G_TYPE_INT, G_TYPE_STRING);
241
242   param_spec = g_param_spec_object ("connection",
243     "connection", "The telepathy connection",
244     TP_TYPE_CONNECTION,
245     G_PARAM_CONSTRUCT_ONLY |
246     G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
247   g_object_class_install_property (object_class, PROP_CONNECTION,
248                                   param_spec);
249
250   param_spec = g_param_spec_object ("channel",
251     "channel", "The telepathy channel",
252     TP_TYPE_CHANNEL,
253     G_PARAM_CONSTRUCT_ONLY |
254     G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
255   g_object_class_install_property (object_class, PROP_CHANNEL,
256                                   param_spec);
257
258   param_spec = g_param_spec_object ("channel-wrapper",
259     "channel wrapper", "The empathy specific channel wrapper",
260     G_TYPE_OBJECT,
261     G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
262   g_object_class_install_property (object_class, PROP_CHANNEL_WRAPPER,
263                                   param_spec);
264
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,
270                                   param_spec);
271
272   param_spec = g_param_spec_boolean ("incoming",
273     "incoming", "Whether or not the channel is incoming",
274     FALSE,
275     G_PARAM_CONSTRUCT_ONLY |
276     G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
277   g_object_class_install_property (object_class, PROP_INCOMING,
278                                   param_spec);
279
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);
285 }
286
287 void
288 empathy_dispatch_operation_dispose (GObject *object)
289 {
290   EmpathyDispatchOperation *self = EMPATHY_DISPATCH_OPERATION (object);
291   EmpathyDispatchOperationPriv *priv =
292     GET_PRIV (self);
293
294   if (priv->dispose_has_run)
295     return;
296
297   priv->dispose_has_run = TRUE;
298
299   g_object_unref (priv->connection);
300
301   if (priv->channel_wrapper != NULL)
302     g_object_unref (priv->channel_wrapper);
303
304   if (priv->ready_handler != 0)
305     g_signal_handler_disconnect (priv->channel_wrapper,
306       priv->invalidated_handler);
307
308
309   g_signal_handler_disconnect (priv->channel, priv->invalidated_handler);
310   g_object_unref (priv->channel);
311
312
313   if (priv->contact != NULL)
314     g_object_unref (priv->contact);
315
316   if (G_OBJECT_CLASS (empathy_dispatch_operation_parent_class)->dispose)
317     G_OBJECT_CLASS (empathy_dispatch_operation_parent_class)->dispose (object);
318 }
319
320 void
321 empathy_dispatch_operation_finalize (GObject *object)
322 {
323   /* free any data held directly by the object here */
324   G_OBJECT_CLASS (empathy_dispatch_operation_parent_class)->finalize (object);
325 }
326
327 static void
328 empathy_dispatch_operation_set_status (EmpathyDispatchOperation *self,
329   EmpathyDispatchOperationState status)
330 {
331   EmpathyDispatchOperationPriv *priv = GET_PRIV (self);
332
333   g_assert (status >= priv->status);
334
335
336   if (priv->status != status)
337     {
338       DEBUG ("Dispatch operation %s status: %d -> %d",
339         empathy_dispatch_operation_get_object_path (self),
340         priv->status, status);
341
342       priv->status = status;
343       g_object_notify (G_OBJECT (self), "status");
344
345       if (status == EMPATHY_DISPATCHER_OPERATION_STATE_PENDING)
346         g_signal_emit (self, signals[READY], 0);
347     }
348 }
349
350 static void
351 empathy_dispatcher_operation_tp_chat_ready_cb (GObject *object,
352   GParamSpec *spec, gpointer user_data)
353 {
354   EmpathyDispatchOperation *self = EMPATHY_DISPATCH_OPERATION (user_data);
355   EmpathyDispatchOperationPriv *priv = GET_PRIV (self);
356
357   if (!empathy_tp_chat_is_ready (EMPATHY_TP_CHAT (priv->channel_wrapper)))
358     return;
359
360   g_signal_handler_disconnect (priv->channel_wrapper, priv->ready_handler);
361   priv->ready_handler = 0;
362
363   empathy_dispatch_operation_set_status (self,
364     EMPATHY_DISPATCHER_OPERATION_STATE_PENDING);
365 }
366
367 static void
368 empathy_dispatch_operation_channel_ready_cb (TpChannel *channel,
369   const GError *error, gpointer user_data)
370 {
371   EmpathyDispatchOperation *self = EMPATHY_DISPATCH_OPERATION (user_data);
372   EmpathyDispatchOperationPriv *priv = GET_PRIV (self);
373   GQuark channel_type;
374
375   /* The error will be handled in empathy_dispatch_operation_invalidated */
376   if (error != NULL)
377     return;
378
379   g_assert (channel == priv->channel);
380
381   /* If the channel wrapper is defined, we assume it's ready */
382   if (priv->channel_wrapper != NULL)
383     goto ready;
384
385   channel_type = tp_channel_get_channel_type_id (channel);
386
387   if (channel_type == TP_IFACE_QUARK_CHANNEL_TYPE_TEXT)
388     {
389       EmpathyTpChat *chat= empathy_tp_chat_new (channel);
390       priv->channel_wrapper = G_OBJECT (chat);
391
392       if (!empathy_tp_chat_is_ready (chat))
393         {
394           priv->ready_handler = g_signal_connect (chat, "notify::ready",
395             G_CALLBACK (empathy_dispatcher_operation_tp_chat_ready_cb), self);
396           return;
397         }
398
399     }
400   else if (channel_type == TP_IFACE_QUARK_CHANNEL_TYPE_STREAMED_MEDIA)
401     {
402        EmpathyTpCall *call = empathy_tp_call_new (channel);
403        priv->channel_wrapper = G_OBJECT (call);
404
405     }
406   else if (channel_type == TP_IFACE_QUARK_CHANNEL_TYPE_FILE_TRANSFER)
407     {
408        EmpathyTpFile *file = empathy_tp_file_new (channel);
409        priv->channel_wrapper = G_OBJECT (file);
410     }
411
412 ready:
413   empathy_dispatch_operation_set_status (self,
414     EMPATHY_DISPATCHER_OPERATION_STATE_PENDING);
415 }
416
417 EmpathyDispatchOperation *
418 empathy_dispatch_operation_new (TpConnection *connection, TpChannel *channel,
419   EmpathyContact *contact, gboolean incoming)
420 {
421   return empathy_dispatch_operation_new_with_wrapper (connection, channel,
422     contact, incoming, NULL);
423 }
424
425 EmpathyDispatchOperation *
426 empathy_dispatch_operation_new_with_wrapper (TpConnection *connection,
427   TpChannel *channel, EmpathyContact *contact, gboolean incoming,
428   GObject *wrapper)
429 {
430   g_return_val_if_fail (connection != NULL, NULL);
431   g_return_val_if_fail (channel != NULL, NULL);
432
433   return EMPATHY_DISPATCH_OPERATION (
434     g_object_new (EMPATHY_TYPE_DISPATCH_OPERATION,
435       "connection", connection,
436       "channel", channel,
437       "channel-wrapper", wrapper,
438       "contact", contact,
439       "incoming", incoming,
440       NULL));
441 }
442
443 void
444 empathy_dispatch_operation_start (EmpathyDispatchOperation *operation)
445 {
446   EmpathyDispatchOperationPriv *priv;
447
448   g_return_if_fail (EMPATHY_IS_DISPATCH_OPERATION (operation));
449
450   priv = GET_PRIV (operation);
451
452   g_return_if_fail (
453     priv->status == EMPATHY_DISPATCHER_OPERATION_STATE_PENDING);
454
455   if (priv->incoming && !priv->approved)
456     empathy_dispatch_operation_set_status (operation,
457       EMPATHY_DISPATCHER_OPERATION_STATE_APPROVING);
458   else
459     empathy_dispatch_operation_set_status (operation,
460       EMPATHY_DISPATCHER_OPERATION_STATE_DISPATCHING);
461 }
462
463 void
464 empathy_dispatch_operation_approve (EmpathyDispatchOperation *operation)
465 {
466   EmpathyDispatchOperationPriv *priv;
467
468   g_return_if_fail (EMPATHY_IS_DISPATCH_OPERATION (operation));
469
470   priv = GET_PRIV (operation);
471
472   if (priv->status == EMPATHY_DISPATCHER_OPERATION_STATE_APPROVING)
473     {
474       DEBUG ("Approving operation %s",
475         empathy_dispatch_operation_get_object_path (operation));
476
477       empathy_dispatch_operation_set_status (operation,
478         EMPATHY_DISPATCHER_OPERATION_STATE_DISPATCHING);
479
480       g_signal_emit (operation, signals[APPROVED], 0);
481     }
482   else if (priv->status < EMPATHY_DISPATCHER_OPERATION_STATE_APPROVING)
483     {
484       DEBUG ("Pre-approving operation %s",
485         empathy_dispatch_operation_get_object_path (operation));
486       priv->approved = TRUE;
487     }
488   else
489     {
490       DEBUG (
491         "Ignoring approval for %s as it's already past the approval stage",
492         empathy_dispatch_operation_get_object_path (operation));
493     }
494 }
495
496 /* Returns whether or not the operation was successfully claimed */
497 gboolean
498 empathy_dispatch_operation_claim (EmpathyDispatchOperation *operation)
499 {
500   EmpathyDispatchOperationPriv *priv;
501
502   g_return_val_if_fail (EMPATHY_IS_DISPATCH_OPERATION (operation), FALSE);
503
504   priv = GET_PRIV (operation);
505
506   if (priv->status == EMPATHY_DISPATCHER_OPERATION_STATE_CLAIMED)
507     return FALSE;
508
509   empathy_dispatch_operation_set_status (operation,
510     EMPATHY_DISPATCHER_OPERATION_STATE_CLAIMED);
511
512   g_signal_emit (operation, signals[CLAIMED], 0);
513
514   return TRUE;
515 }
516
517 TpConnection *
518 empathy_dispatch_operation_get_tp_connection (
519   EmpathyDispatchOperation *operation)
520 {
521   EmpathyDispatchOperationPriv *priv;
522
523   g_return_val_if_fail (EMPATHY_IS_DISPATCH_OPERATION (operation), NULL);
524
525   priv = GET_PRIV (operation);
526
527   return g_object_ref (priv->connection);
528 }
529
530 TpChannel *
531 empathy_dispatch_operation_get_channel (EmpathyDispatchOperation *operation)
532 {
533   EmpathyDispatchOperationPriv *priv;
534
535   g_return_val_if_fail (EMPATHY_IS_DISPATCH_OPERATION (operation), NULL);
536
537   priv = GET_PRIV (operation);
538
539   return priv->channel;
540 }
541
542 GObject *
543 empathy_dispatch_operation_get_channel_wrapper (
544   EmpathyDispatchOperation *operation)
545 {
546   EmpathyDispatchOperationPriv *priv;
547
548   g_return_val_if_fail (EMPATHY_IS_DISPATCH_OPERATION (operation), NULL);
549
550   priv = GET_PRIV (operation);
551
552   return priv->channel_wrapper;
553 }
554
555 const gchar *
556 empathy_dispatch_operation_get_channel_type (
557   EmpathyDispatchOperation *operation)
558 {
559   EmpathyDispatchOperationPriv *priv;
560
561   g_return_val_if_fail (EMPATHY_IS_DISPATCH_OPERATION (operation), NULL);
562
563   priv = GET_PRIV (operation);
564
565   return tp_channel_get_channel_type (priv->channel);
566 }
567
568 GQuark
569 empathy_dispatch_operation_get_channel_type_id (
570   EmpathyDispatchOperation *operation)
571 {
572   EmpathyDispatchOperationPriv *priv;
573
574   g_return_val_if_fail (EMPATHY_IS_DISPATCH_OPERATION (operation), 0);
575
576   priv = GET_PRIV (operation);
577
578   return tp_channel_get_channel_type_id (priv->channel);
579 }
580
581 const gchar *
582 empathy_dispatch_operation_get_object_path (
583   EmpathyDispatchOperation *operation)
584 {
585   EmpathyDispatchOperationPriv *priv;
586
587   g_return_val_if_fail (EMPATHY_IS_DISPATCH_OPERATION (operation), NULL);
588
589   priv = GET_PRIV (operation);
590
591   return tp_proxy_get_object_path (TP_PROXY (priv->channel));
592 }
593
594 EmpathyDispatchOperationState
595 empathy_dispatch_operation_get_status (EmpathyDispatchOperation *operation)
596 {
597   EmpathyDispatchOperationPriv *priv;
598
599   g_return_val_if_fail (EMPATHY_IS_DISPATCH_OPERATION (operation),
600     EMPATHY_DISPATCHER_OPERATION_STATE_PREPARING);
601
602   priv = GET_PRIV (operation);
603
604   return priv->status;
605 }
606
607 gboolean
608 empathy_dispatch_operation_is_incoming (EmpathyDispatchOperation *operation)
609 {
610   EmpathyDispatchOperationPriv *priv;
611
612   g_return_val_if_fail (EMPATHY_IS_DISPATCH_OPERATION (operation), FALSE);
613
614   priv = GET_PRIV (operation);
615
616   return priv->incoming;
617 }