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