1 /*
2 * objects for AddDispatchOperation calls
3 *
4 * Copyright © 2010 Collabora Ltd. <http://www.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 * SECTION:add-dispatch-operation-context
23 * @title: TpAddDispatchOperationContext
24 * @short_description: context of a Approver.AddDispatchOperation() call
25 *
26 * Object used to represent the context of a Approver.AddDispatchOperation()
27 * D-Bus call on a #TpBaseClient.
28 */
29
30 /**
31 * TpAddDispatchOperationContext:
32 *
33 * Data structure representing the context of a Approver.AddDispatchOperation()
34 * call.
35 *
36 * Since: 0.11.5
37 */
38
39 /**
40 * TpAddDispatchOperationContextClass:
41 *
42 * The class of a #TpAddDispatchOperationContext.
43 *
44 * Since: 0.11.5
45 */
46
47 #include "config.h"
48
49 #include "telepathy-glib/add-dispatch-operation-context-internal.h"
50 #include "telepathy-glib/add-dispatch-operation-context.h"
51
52 #include <telepathy-glib/channel.h>
53 #include <telepathy-glib/dbus.h>
54 #include <telepathy-glib/gtypes.h>
55
56 #define DEBUG_FLAG TP_DEBUG_CLIENT
57 #include "telepathy-glib/debug-internal.h"
58 #include "telepathy-glib/util-internal.h"
59
60 struct _TpAddDispatchOperationContextClass {
61 /*<private>*/
62 GObjectClass parent_class;
63 };
64
65 G_DEFINE_TYPE(TpAddDispatchOperationContext,
66 tp_add_dispatch_operation_context, G_TYPE_OBJECT)
67
68 enum {
69 PROP_ACCOUNT = 1,
70 PROP_CONNECTION,
71 PROP_CHANNELS,
72 PROP_DISPATCH_OPERATION,
73 PROP_DBUS_CONTEXT,
74 N_PROPS
75 };
76
77 struct _TpAddDispatchOperationContextPrivate
78 {
79 TpAddDispatchOperationContextState state;
80 GSimpleAsyncResult *result;
81 DBusGMethodInvocation *dbus_context;
82
83 /* Number of calls we are waiting they return. Once they have all returned
84 * the context is considered as prepared */
85 guint num_pending;
86 };
87
88 static void
tp_add_dispatch_operation_context_init(TpAddDispatchOperationContext * self)89 tp_add_dispatch_operation_context_init (TpAddDispatchOperationContext *self)
90 {
91 self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
92 TP_TYPE_ADD_DISPATCH_OPERATION_CONTEXT,
93 TpAddDispatchOperationContextPrivate);
94
95 self->priv->state = TP_ADD_DISPATCH_OPERATION_CONTEXT_STATE_NONE;
96 }
97
98 static void
tp_add_dispatch_operation_context_dispose(GObject * object)99 tp_add_dispatch_operation_context_dispose (GObject *object)
100 {
101 TpAddDispatchOperationContext *self = TP_ADD_DISPATCH_OPERATION_CONTEXT (
102 object);
103 void (*dispose) (GObject *) =
104 G_OBJECT_CLASS (tp_add_dispatch_operation_context_parent_class)->dispose;
105
106 if (self->priv->state == TP_ADD_DISPATCH_OPERATION_CONTEXT_STATE_NONE ||
107 self->priv->state == TP_ADD_DISPATCH_OPERATION_CONTEXT_STATE_DELAYED)
108 {
109 GError error = { TP_ERROR, TP_ERROR_NOT_IMPLEMENTED,
110 "Disposing the TpAddDispatchOperationContext" };
111
112 WARNING ("Disposing a context in the %s state",
113 self->priv->state == TP_ADD_DISPATCH_OPERATION_CONTEXT_STATE_NONE ?
114 "none": "delayed");
115
116 tp_add_dispatch_operation_context_fail (self, &error);
117 }
118
119 if (self->account != NULL)
120 {
121 g_object_unref (self->account);
122 self->account = NULL;
123 }
124
125 if (self->connection != NULL)
126 {
127 g_object_unref (self->connection);
128 self->connection = NULL;
129 }
130
131 if (self->channels != NULL)
132 {
133 g_ptr_array_unref (self->channels);
134 self->channels = NULL;
135 }
136
137 if (self->dispatch_operation != NULL)
138 {
139 g_object_unref (self->dispatch_operation);
140 self->dispatch_operation = NULL;
141 }
142
143 if (self->priv->result != NULL)
144 {
145 g_object_unref (self->priv->result);
146 self->priv->result = NULL;
147 }
148
149 if (dispose != NULL)
150 dispose (object);
151 }
152
153 static void
tp_add_dispatch_operation_context_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)154 tp_add_dispatch_operation_context_get_property (GObject *object,
155 guint property_id,
156 GValue *value,
157 GParamSpec *pspec)
158 {
159 TpAddDispatchOperationContext *self = TP_ADD_DISPATCH_OPERATION_CONTEXT (
160 object);
161
162 switch (property_id)
163 {
164 case PROP_ACCOUNT:
165 g_value_set_object (value, self->account);
166 break;
167
168 case PROP_CONNECTION:
169 g_value_set_object (value, self->connection);
170 break;
171
172 case PROP_CHANNELS:
173 g_value_set_boxed (value, self->channels);
174 break;
175
176 case PROP_DISPATCH_OPERATION:
177 g_value_set_object (value, self->dispatch_operation);
178 break;
179
180 default:
181 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
182 break;
183 }
184 }
185
186 static void
tp_add_dispatch_operation_context_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)187 tp_add_dispatch_operation_context_set_property (GObject *object,
188 guint property_id,
189 const GValue *value,
190 GParamSpec *pspec)
191 {
192 TpAddDispatchOperationContext *self = TP_ADD_DISPATCH_OPERATION_CONTEXT (
193 object);
194
195 switch (property_id)
196 {
197 case PROP_ACCOUNT:
198 self->account = g_value_dup_object (value);
199 break;
200
201 case PROP_CONNECTION:
202 self->connection = g_value_dup_object (value);
203 break;
204
205 case PROP_CHANNELS:
206 self->channels = g_value_dup_boxed (value);
207 break;
208
209 case PROP_DISPATCH_OPERATION:
210 self->dispatch_operation = g_value_dup_object (value);
211 break;
212
213 case PROP_DBUS_CONTEXT:
214 self->priv->dbus_context = g_value_get_pointer (value);
215 break;
216
217 default:
218 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
219 break;
220 }
221 }
222
223 static void
tp_add_dispatch_operation_context_constructed(GObject * object)224 tp_add_dispatch_operation_context_constructed (GObject *object)
225 {
226 TpAddDispatchOperationContext *self = TP_ADD_DISPATCH_OPERATION_CONTEXT (
227 object);
228 void (*chain_up) (GObject *) =
229 ((GObjectClass *)
230 tp_add_dispatch_operation_context_parent_class)->constructed;
231
232 if (chain_up != NULL)
233 chain_up (object);
234
235 g_assert (self->account != NULL);
236 g_assert (self->connection != NULL);
237 g_assert (self->channels != NULL);
238 g_assert (self->dispatch_operation != NULL);
239 g_assert (self->priv->dbus_context != NULL);
240 }
241
242 static void
tp_add_dispatch_operation_context_class_init(TpAddDispatchOperationContextClass * cls)243 tp_add_dispatch_operation_context_class_init (
244 TpAddDispatchOperationContextClass *cls)
245 {
246 GObjectClass *object_class = G_OBJECT_CLASS (cls);
247 GParamSpec *param_spec;
248
249 g_type_class_add_private (cls, sizeof (TpAddDispatchOperationContextPrivate));
250
251 object_class->get_property = tp_add_dispatch_operation_context_get_property;
252 object_class->set_property = tp_add_dispatch_operation_context_set_property;
253 object_class->constructed = tp_add_dispatch_operation_context_constructed;
254 object_class->dispose = tp_add_dispatch_operation_context_dispose;
255
256 /**
257 * TpAddDispatchOperationContext:account:
258 *
259 * A #TpAccount object representing the Account of the DispatchOperation
260 * that has been passed to AddDispatchOperation.
261 * Read-only except during construction.
262 *
263 * This property can't be %NULL.
264 *
265 * Since: 0.11.5
266 */
267 param_spec = g_param_spec_object ("account", "TpAccount",
268 "The TpAccount of the context",
269 TP_TYPE_ACCOUNT,
270 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
271 g_object_class_install_property (object_class, PROP_ACCOUNT,
272 param_spec);
273
274 /**
275 * TpAddDispatchOperationContext:connection:
276 *
277 * A #TpConnection object representing the Connection of the DispatchOperation
278 * that has been passed to AddDispatchOperation.
279 * Read-only except during construction.
280 *
281 * This property can't be %NULL.
282 *
283 * Since: 0.11.5
284 */
285 param_spec = g_param_spec_object ("connection", "TpConnection",
286 "The TpConnection of the context",
287 TP_TYPE_CONNECTION,
288 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
289 g_object_class_install_property (object_class, PROP_CONNECTION,
290 param_spec);
291
292 /**
293 * TpAddDispatchOperationContext:channels:
294 *
295 * A #GPtrArray containing #TpChannel objects representing the channels
296 * that have been passed to AddDispatchOperation.
297 * Read-only except during construction.
298 *
299 * This property can't be %NULL.
300 *
301 * Since: 0.11.5
302 */
303 param_spec = g_param_spec_boxed ("channels", "GPtrArray of TpChannel",
304 "The TpChannels that have been passed to AddDispatchOperation",
305 G_TYPE_PTR_ARRAY,
306 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
307 g_object_class_install_property (object_class, PROP_CHANNELS,
308 param_spec);
309
310 /**
311 * TpAddDispatchOperationContext:dispatch-operation:
312 *
313 * A #TpChannelDispatchOperation object representing the
314 * ChannelDispatchOperation that has been passed to AddDispatchOperation.
315 * Read-only except during construction.
316 *
317 * This property can't be %NULL.
318 *
319 * Since: 0.11.5
320 */
321 param_spec = g_param_spec_object ("dispatch-operation",
322 "TpChannelDispatchOperation",
323 "The TpChannelDispatchOperation that has been passed to "
324 "AddDispatchOperation",
325 TP_TYPE_CHANNEL_DISPATCH_OPERATION,
326 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
327 g_object_class_install_property (object_class, PROP_DISPATCH_OPERATION,
328 param_spec);
329
330 /**
331 * TpAddDispatchOperationContext:dbus-context: (skip)
332 *
333 * The #DBusGMethodInvocation representing the D-Bus context of the
334 * AddDispatchOperation call.
335 * Can only be written during construction.
336 *
337 * Since: 0.11.5
338 */
339 param_spec = g_param_spec_pointer ("dbus-context", "D-Bus context",
340 "The DBusGMethodInvocation associated with the AddDispatchOperation call",
341 G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
342 g_object_class_install_property (object_class, PROP_DBUS_CONTEXT,
343 param_spec);
344 }
345
346 TpAddDispatchOperationContext *
_tp_add_dispatch_operation_context_new(TpAccount * account,TpConnection * connection,GPtrArray * channels,TpChannelDispatchOperation * dispatch_operation,DBusGMethodInvocation * dbus_context)347 _tp_add_dispatch_operation_context_new (
348 TpAccount *account,
349 TpConnection *connection,
350 GPtrArray *channels,
351 TpChannelDispatchOperation *dispatch_operation,
352 DBusGMethodInvocation *dbus_context)
353 {
354 return g_object_new (TP_TYPE_ADD_DISPATCH_OPERATION_CONTEXT,
355 "account", account,
356 "connection", connection,
357 "channels", channels,
358 "dispatch-operation", dispatch_operation,
359 "dbus-context", dbus_context,
360 NULL);
361 }
362
363 /**
364 * tp_add_dispatch_operation_context_accept:
365 * @self: a #TpAddDispatchOperationContext
366 *
367 * Called by #TpBaseClientClassAddDispatchOperationImpl when it's done so
368 * the D-Bus method can return.
369 *
370 * Since: 0.11.5
371 */
372 void
tp_add_dispatch_operation_context_accept(TpAddDispatchOperationContext * self)373 tp_add_dispatch_operation_context_accept (TpAddDispatchOperationContext *self)
374 {
375 g_return_if_fail (self->priv->state ==
376 TP_ADD_DISPATCH_OPERATION_CONTEXT_STATE_NONE
377 || self->priv->state == TP_ADD_DISPATCH_OPERATION_CONTEXT_STATE_DELAYED);
378 g_return_if_fail (self->priv->dbus_context != NULL);
379
380 self->priv->state = TP_ADD_DISPATCH_OPERATION_CONTEXT_STATE_DONE;
381 dbus_g_method_return (self->priv->dbus_context);
382
383 self->priv->dbus_context = NULL;
384 }
385
386 /**
387 * tp_add_dispatch_operation_context_fail:
388 * @self: a #TpAddDispatchOperationContext
389 * @error: the error to return from the method
390 *
391 * Called by #TpBaseClientClassAddDispatchOperationImpl to raise a D-Bus error.
392 *
393 * Since: 0.11.5
394 */
395 void
tp_add_dispatch_operation_context_fail(TpAddDispatchOperationContext * self,const GError * error)396 tp_add_dispatch_operation_context_fail (TpAddDispatchOperationContext *self,
397 const GError *error)
398 {
399 g_return_if_fail (self->priv->state ==
400 TP_ADD_DISPATCH_OPERATION_CONTEXT_STATE_NONE
401 || self->priv->state == TP_ADD_DISPATCH_OPERATION_CONTEXT_STATE_DELAYED);
402 g_return_if_fail (self->priv->dbus_context != NULL);
403
404 self->priv->state = TP_ADD_DISPATCH_OPERATION_CONTEXT_STATE_FAILED;
405 dbus_g_method_return_error (self->priv->dbus_context, error);
406
407 self->priv->dbus_context = NULL;
408 }
409
410 /**
411 * tp_add_dispatch_operation_context_delay:
412 * @self: a #TpAddDispatchOperationContext
413 *
414 * Called by #TpBaseClientClassAddDispatchOperationImpl to indicate that it
415 * implements the method in an async way. The caller must take a reference
416 * to the #TpAddDispatchOperationContext before calling this function, and
417 * is responsible for calling either
418 * tp_add_dispatch_operation_context_accept() or
419 * tp_add_dispatch_operation_context_fail() later.
420 *
421 * Since: 0.11.5
422 */
423 void
tp_add_dispatch_operation_context_delay(TpAddDispatchOperationContext * self)424 tp_add_dispatch_operation_context_delay (TpAddDispatchOperationContext *self)
425 {
426 g_return_if_fail (self->priv->state ==
427 TP_ADD_DISPATCH_OPERATION_CONTEXT_STATE_NONE);
428
429 self->priv->state = TP_ADD_DISPATCH_OPERATION_CONTEXT_STATE_DELAYED;
430 }
431
432 TpAddDispatchOperationContextState
_tp_add_dispatch_operation_context_get_state(TpAddDispatchOperationContext * self)433 _tp_add_dispatch_operation_context_get_state (
434 TpAddDispatchOperationContext *self)
435 {
436 return self->priv->state;
437 }
438
439 static gboolean
context_is_prepared(TpAddDispatchOperationContext * self)440 context_is_prepared (TpAddDispatchOperationContext *self)
441 {
442 return self->priv->num_pending == 0;
443 }
444
445 static void
context_check_prepare(TpAddDispatchOperationContext * self)446 context_check_prepare (TpAddDispatchOperationContext *self)
447 {
448 if (!context_is_prepared (self))
449 return;
450
451 /* is prepared */
452 g_simple_async_result_complete (self->priv->result);
453
454 g_object_unref (self->priv->result);
455 self->priv->result = NULL;
456 }
457
458 static void
cdo_prepare_cb(GObject * source,GAsyncResult * result,gpointer user_data)459 cdo_prepare_cb (GObject *source,
460 GAsyncResult *result,
461 gpointer user_data)
462 {
463 TpAddDispatchOperationContext *self = user_data;
464 GError *error = NULL;
465
466 if (self->priv->result == NULL)
467 goto out;
468
469 if (!tp_proxy_prepare_finish (source, result, &error))
470 {
471 DEBUG ("Failed to prepare ChannelDispatchOperation: %s", error->message);
472
473 g_error_free (error);
474 }
475
476 self->priv->num_pending--;
477 context_check_prepare (self);
478
479 out:
480 g_object_unref (self);
481 }
482
483 static void
account_prepare_cb(GObject * source,GAsyncResult * result,gpointer user_data)484 account_prepare_cb (GObject *source,
485 GAsyncResult *result,
486 gpointer user_data)
487 {
488 TpAddDispatchOperationContext *self = user_data;
489 GError *error = NULL;
490
491 if (self->priv->result == NULL)
492 goto out;
493
494 if (!tp_proxy_prepare_finish (source, result, &error))
495 {
496 DEBUG ("Failed to prepare account: %s", error->message);
497 g_error_free (error);
498 }
499
500 self->priv->num_pending--;
501 context_check_prepare (self);
502
503 out:
504 g_object_unref (self);
505 }
506
507 static void
conn_prepare_cb(GObject * source,GAsyncResult * result,gpointer user_data)508 conn_prepare_cb (GObject *source,
509 GAsyncResult *result,
510 gpointer user_data)
511 {
512 TpAddDispatchOperationContext *self = user_data;
513 GError *error = NULL;
514
515 if (self->priv->result == NULL)
516 goto out;
517
518 if (!tp_proxy_prepare_finish (source, result, &error))
519 {
520 DEBUG ("Failed to prepare connection: %s", error->message);
521 g_error_free (error);
522 }
523
524 self->priv->num_pending--;
525 context_check_prepare (self);
526
527 out:
528 g_object_unref (self);
529 }
530
531 static void
adoc_channel_prepare_cb(GObject * source,GAsyncResult * result,gpointer user_data)532 adoc_channel_prepare_cb (GObject *source,
533 GAsyncResult *result,
534 gpointer user_data)
535 {
536 TpAddDispatchOperationContext *self = user_data;
537 GError *error = NULL;
538
539 if (self->priv->result == NULL)
540 goto out;
541
542 if (!tp_proxy_prepare_finish (source, result, &error))
543 {
544 DEBUG ("Failed to prepare channel: %s", error->message);
545
546 g_error_free (error);
547 }
548
549 self->priv->num_pending--;
550 context_check_prepare (self);
551
552 out:
553 g_object_unref (self);
554 }
555
556 static void
context_prepare(TpAddDispatchOperationContext * self,const GQuark * account_features,const GQuark * connection_features,const GQuark * channel_features)557 context_prepare (TpAddDispatchOperationContext *self,
558 const GQuark *account_features,
559 const GQuark *connection_features,
560 const GQuark *channel_features)
561 {
562 GQuark cdo_features[] = { TP_CHANNEL_DISPATCH_OPERATION_FEATURE_CORE, 0 };
563 guint i;
564
565 self->priv->num_pending = 3;
566
567 tp_proxy_prepare_async (self->account, account_features,
568 account_prepare_cb, g_object_ref (self));
569
570 tp_proxy_prepare_async (self->connection, connection_features,
571 conn_prepare_cb, g_object_ref (self));
572
573 tp_proxy_prepare_async (self->dispatch_operation, cdo_features,
574 cdo_prepare_cb, g_object_ref (self));
575
576 for (i = 0; i < self->channels->len; i++)
577 {
578 TpChannel *channel = g_ptr_array_index (self->channels, i);
579
580 self->priv->num_pending++;
581
582 tp_proxy_prepare_async (channel, channel_features,
583 adoc_channel_prepare_cb, g_object_ref (self));
584 }
585 }
586
587 void
_tp_add_dispatch_operation_context_prepare_async(TpAddDispatchOperationContext * self,const GQuark * account_features,const GQuark * connection_features,const GQuark * channel_features,GAsyncReadyCallback callback,gpointer user_data)588 _tp_add_dispatch_operation_context_prepare_async (
589 TpAddDispatchOperationContext *self,
590 const GQuark *account_features,
591 const GQuark *connection_features,
592 const GQuark *channel_features,
593 GAsyncReadyCallback callback,
594 gpointer user_data)
595 {
596 g_return_if_fail (TP_IS_ADD_DISPATCH_OPERATION_CONTEXT (self));
597 /* This is only used once, by TpBaseClient, so for simplicity, we only
598 * allow one asynchronous preparation */
599 g_return_if_fail (self->priv->result == NULL);
600
601 self->priv->result = g_simple_async_result_new (G_OBJECT (self),
602 callback, user_data, _tp_add_dispatch_operation_context_prepare_async);
603
604 context_prepare (self, account_features, connection_features,
605 channel_features);
606 }
607
608 gboolean
_tp_add_dispatch_operation_context_prepare_finish(TpAddDispatchOperationContext * self,GAsyncResult * result,GError ** error)609 _tp_add_dispatch_operation_context_prepare_finish (
610 TpAddDispatchOperationContext *self,
611 GAsyncResult *result,
612 GError **error)
613 {
614 _tp_implement_finish_void (self,
615 _tp_add_dispatch_operation_context_prepare_async);
616 }
617