1 /*
2  * Context objects for TpBaseClient calls
3  *
4  * Copyright © 2010 Collabora Ltd.
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:observe-channels-context
23  * @title: TpObserveChannelsContext
24  * @short_description: context of a Observer.ObserveChannels() call
25  *
26  * Object used to represent the context of a Observer.ObserveChannels()
27  * D-Bus call on a #TpBaseClient.
28  */
29 
30 /**
31  * TpObserveChannelsContext:
32  *
33  * Data structure representing the context of a Observer.ObserveChannels()
34  * call.
35  *
36  * Since: 0.11.5
37  */
38 
39 /**
40  * TpObserveChannelsContextClass:
41  *
42  * The class of a #TpObserveChannelsContext.
43  *
44  * Since: 0.11.5
45  */
46 
47 #include "config.h"
48 
49 #include "telepathy-glib/observe-channels-context-internal.h"
50 #include "telepathy-glib/observe-channels-context.h"
51 
52 #include <telepathy-glib/channel.h>
53 #include <telepathy-glib/channel-request.h>
54 #include <telepathy-glib/dbus.h>
55 #include <telepathy-glib/gtypes.h>
56 #include <telepathy-glib/util-internal.h>
57 
58 #define DEBUG_FLAG TP_DEBUG_CLIENT
59 #include "telepathy-glib/debug-internal.h"
60 
61 struct _TpObserveChannelsContextClass {
62     /*<private>*/
63     GObjectClass parent_class;
64 };
65 
66 G_DEFINE_TYPE(TpObserveChannelsContext, tp_observe_channels_context,
67     G_TYPE_OBJECT)
68 
69 enum {
70     PROP_ACCOUNT = 1,
71     PROP_CONNECTION,
72     PROP_CHANNELS,
73     PROP_DISPATCH_OPERATION,
74     PROP_REQUESTS,
75     PROP_OBSERVER_INFO,
76     PROP_DBUS_CONTEXT,
77     N_PROPS
78 };
79 
80 struct _TpObserveChannelsContextPrivate
81 {
82   TpObserveChannelsContextState state;
83   GSimpleAsyncResult *result;
84   DBusGMethodInvocation *dbus_context;
85 
86   /* Number of calls we are waiting they return. Once they have all returned
87    * the context is considered as prepared */
88   guint num_pending;
89 };
90 
91 static void
tp_observe_channels_context_init(TpObserveChannelsContext * self)92 tp_observe_channels_context_init (TpObserveChannelsContext *self)
93 {
94   self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
95       TP_TYPE_OBSERVE_CHANNELS_CONTEXT, TpObserveChannelsContextPrivate);
96 
97   self->priv->state = TP_OBSERVE_CHANNELS_CONTEXT_STATE_NONE;
98 }
99 
100 static void
tp_observe_channels_context_dispose(GObject * object)101 tp_observe_channels_context_dispose (GObject *object)
102 {
103   TpObserveChannelsContext *self = TP_OBSERVE_CHANNELS_CONTEXT (object);
104   void (*dispose) (GObject *) =
105     G_OBJECT_CLASS (tp_observe_channels_context_parent_class)->dispose;
106 
107   if (self->priv->state == TP_OBSERVE_CHANNELS_CONTEXT_STATE_NONE ||
108       self->priv->state == TP_OBSERVE_CHANNELS_CONTEXT_STATE_DELAYED)
109     {
110       GError error = { TP_ERROR, TP_ERROR_NOT_IMPLEMENTED,
111           "Disposing the TpObserveChannelsContext" };
112 
113       WARNING ("Disposing a context in the %s state",
114           self->priv->state == TP_OBSERVE_CHANNELS_CONTEXT_STATE_NONE ?
115           "none": "delayed");
116 
117       tp_observe_channels_context_fail (self, &error);
118     }
119 
120   if (self->account != NULL)
121     {
122       g_object_unref (self->account);
123       self->account = NULL;
124     }
125 
126   if (self->connection != NULL)
127     {
128       g_object_unref (self->connection);
129       self->connection = NULL;
130     }
131 
132   if (self->channels != NULL)
133     {
134       g_ptr_array_unref (self->channels);
135       self->channels = NULL;
136     }
137 
138   if (self->dispatch_operation != NULL)
139     {
140       g_object_unref (self->dispatch_operation);
141       self->dispatch_operation = NULL;
142     }
143 
144   if (self->requests != NULL)
145     {
146       g_ptr_array_foreach (self->requests, (GFunc) g_object_unref, NULL);
147       g_ptr_array_unref (self->requests);
148       self->requests = NULL;
149     }
150 
151   if (self->observer_info != NULL)
152     {
153       g_hash_table_unref (self->observer_info);
154       self->observer_info = NULL;
155     }
156 
157   if (self->priv->result != NULL)
158     {
159       g_object_unref (self->priv->result);
160       self->priv->result = NULL;
161     }
162 
163   if (dispose != NULL)
164     dispose (object);
165 }
166 
167 static void
tp_observe_channels_context_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)168 tp_observe_channels_context_get_property (GObject *object,
169     guint property_id,
170     GValue *value,
171     GParamSpec *pspec)
172 {
173   TpObserveChannelsContext *self = TP_OBSERVE_CHANNELS_CONTEXT (object);
174 
175   switch (property_id)
176     {
177       case PROP_ACCOUNT:
178         g_value_set_object (value, self->account);
179         break;
180       case PROP_CONNECTION:
181         g_value_set_object (value, self->connection);
182         break;
183       case PROP_CHANNELS:
184         g_value_set_boxed (value, self->channels);
185         break;
186       case PROP_DISPATCH_OPERATION:
187         g_value_set_object (value, self->dispatch_operation);
188         break;
189       case PROP_REQUESTS:
190         g_value_set_boxed (value, self->requests);
191         break;
192       case PROP_OBSERVER_INFO:
193         g_value_set_boxed (value, self->observer_info);
194         break;
195       default:
196         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
197         break;
198   }
199 }
200 
201 static void
tp_observe_channels_context_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)202 tp_observe_channels_context_set_property (GObject *object,
203     guint property_id,
204     const GValue *value,
205     GParamSpec *pspec)
206 {
207   TpObserveChannelsContext *self = TP_OBSERVE_CHANNELS_CONTEXT (object);
208 
209   switch (property_id)
210     {
211       case PROP_ACCOUNT:
212         self->account = g_value_dup_object (value);
213         break;
214       case PROP_CONNECTION:
215         self->connection = g_value_dup_object (value);
216         break;
217       case PROP_CHANNELS:
218         self->channels = g_value_dup_boxed (value);
219         break;
220       case PROP_DISPATCH_OPERATION:
221         self->dispatch_operation = g_value_dup_object (value);
222         break;
223       case PROP_REQUESTS:
224         self->requests = g_value_dup_boxed (value);
225         g_ptr_array_foreach (self->requests, (GFunc) g_object_ref, NULL);
226         break;
227       case PROP_DBUS_CONTEXT:
228         self->priv->dbus_context = g_value_get_pointer (value);
229         break;
230       case PROP_OBSERVER_INFO:
231         self->observer_info = g_value_dup_boxed (value);
232         break;
233       default:
234         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
235         break;
236   }
237 }
238 
239 static void
tp_observe_channels_context_constructed(GObject * object)240 tp_observe_channels_context_constructed (GObject *object)
241 {
242   TpObserveChannelsContext *self = TP_OBSERVE_CHANNELS_CONTEXT (object);
243   void (*chain_up) (GObject *) =
244     ((GObjectClass *) tp_observe_channels_context_parent_class)->constructed;
245 
246   if (chain_up != NULL)
247     chain_up (object);
248 
249   g_assert (self->account != NULL);
250   g_assert (self->connection != NULL);
251   g_assert (self->channels != NULL);
252   g_assert (self->requests != NULL);
253   g_assert (self->observer_info != NULL);
254   g_assert (self->priv->dbus_context != NULL);
255 
256   /* self->dispatch_operation may be NULL (channels were requested) */
257 }
258 
259 static void
tp_observe_channels_context_class_init(TpObserveChannelsContextClass * cls)260 tp_observe_channels_context_class_init (TpObserveChannelsContextClass *cls)
261 {
262   GObjectClass *object_class = G_OBJECT_CLASS (cls);
263   GParamSpec *param_spec;
264 
265   g_type_class_add_private (cls, sizeof (TpObserveChannelsContextPrivate));
266 
267   object_class->get_property = tp_observe_channels_context_get_property;
268   object_class->set_property = tp_observe_channels_context_set_property;
269   object_class->constructed = tp_observe_channels_context_constructed;
270   object_class->dispose = tp_observe_channels_context_dispose;
271 
272   /**
273    * TpObserveChannelsContext:account:
274    *
275    * A #TpAccount object representing the Account that has been passed to
276    * ObserveChannels.
277    * Read-only except during construction.
278    *
279    * This property can't be %NULL.
280    *
281    * Since: 0.11.5
282    */
283   param_spec = g_param_spec_object ("account", "TpAccount",
284       "The TpAccount that has been passed to ObserveChannels",
285       TP_TYPE_ACCOUNT,
286       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
287   g_object_class_install_property (object_class, PROP_ACCOUNT,
288       param_spec);
289 
290   /**
291    * TpObserveChannelsContext:connection:
292    *
293    * A #TpConnection object representing the Connection that has been passed
294    * to ObserveChannels.
295    * Read-only except during construction.
296    *
297    * This property can't be %NULL.
298    *
299    * Since: 0.11.5
300    */
301   param_spec = g_param_spec_object ("connection", "TpConnection",
302       "The TpConnection that has been passed to ObserveChannels",
303       TP_TYPE_CONNECTION,
304       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
305   g_object_class_install_property (object_class, PROP_CONNECTION,
306       param_spec);
307 
308   /**
309    * TpObserveChannelsContext:channels:
310    *
311    * A #GPtrArray containing #TpChannel objects representing the channels
312    * that have been passed to ObserveChannels.
313    * Read-only except during construction.
314    *
315    * This property can't be %NULL.
316    *
317    * Since: 0.11.5
318    */
319   param_spec = g_param_spec_boxed ("channels", "GPtrArray of TpChannel",
320       "The TpChannels that have been passed to ObserveChannels",
321       G_TYPE_PTR_ARRAY,
322       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
323   g_object_class_install_property (object_class, PROP_CHANNELS,
324       param_spec);
325 
326   /**
327    * TpObserveChannelsContext:dispatch-operation:
328    *
329    * A #TpChannelDispatchOperation object representing the
330    * ChannelDispatchOperation that has been passed to ObserveChannels,
331    * or %NULL if none has been passed.
332    * Read-only except during construction.
333    *
334    * Since: 0.11.5
335    */
336   param_spec = g_param_spec_object ("dispatch-operation",
337      "TpChannelDispatchOperation",
338      "The TpChannelDispatchOperation that has been passed to ObserveChannels",
339      TP_TYPE_CHANNEL_DISPATCH_OPERATION,
340      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
341   g_object_class_install_property (object_class, PROP_DISPATCH_OPERATION,
342       param_spec);
343 
344   /**
345    * TpObserveChannelsContext:requests:
346    *
347    * A #GPtrArray containing #TpChannelRequest objects representing the
348    * requests that have been passed to ObserveChannels.
349    * Read-only except during construction.
350    *
351    * This property can't be %NULL.
352    *
353    * Since: 0.11.5
354    */
355   param_spec = g_param_spec_boxed ("requests", "GPtrArray of TpChannelRequest",
356       "The TpChannelRequest that have been passed to ObserveChannels",
357       G_TYPE_PTR_ARRAY,
358       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
359   g_object_class_install_property (object_class, PROP_REQUESTS,
360       param_spec);
361 
362   /**
363    * TpObserveChannelsContext:dbus-context: (skip)
364    *
365    * The #DBusGMethodInvocation representing the D-Bus context of the
366    * ObserveChannels call.
367    * Can only be written during construction.
368    *
369    * Since: 0.11.5
370    */
371   param_spec = g_param_spec_pointer ("dbus-context", "D-Bus context",
372       "The DBusGMethodInvocation associated with the ObserveChannels call",
373       G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
374   g_object_class_install_property (object_class, PROP_DBUS_CONTEXT,
375       param_spec);
376 
377   /**
378    * TpObserveChannelsContext:observer-info:
379    *
380    * A #GHashTable where the keys are string and values are GValue instances.
381    * It represents the Observer_Info hash table that has been passed to
382    * ObserveChannels.
383    * It's recommended to use high-level method such as
384    * tp_observe_channels_context_is_recovering() to access to its content.
385    *
386    * This property can't be %NULL.
387    *
388    * Since: 0.11.5
389    */
390   param_spec = g_param_spec_boxed ("observer-info", "Observer info",
391       "The Observer_Info that has been passed to ObserveChannels",
392       TP_HASH_TYPE_STRING_VARIANT_MAP,
393       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
394   g_object_class_install_property (object_class, PROP_OBSERVER_INFO,
395       param_spec);
396 }
397 
398 TpObserveChannelsContext *
_tp_observe_channels_context_new(TpAccount * account,TpConnection * connection,GPtrArray * channels,TpChannelDispatchOperation * dispatch_operation,GPtrArray * requests,GHashTable * observer_info,DBusGMethodInvocation * dbus_context)399 _tp_observe_channels_context_new (
400     TpAccount *account,
401     TpConnection *connection,
402     GPtrArray *channels,
403     TpChannelDispatchOperation *dispatch_operation,
404     GPtrArray *requests,
405     GHashTable *observer_info,
406     DBusGMethodInvocation *dbus_context)
407 {
408   return g_object_new (TP_TYPE_OBSERVE_CHANNELS_CONTEXT,
409       "account", account,
410       "connection", connection,
411       "channels", channels,
412       "dispatch-operation", dispatch_operation,
413       "requests", requests,
414       "observer-info", observer_info,
415       "dbus-context", dbus_context,
416       NULL);
417 }
418 
419 /**
420  * tp_observe_channels_context_accept:
421  * @self: a #TpObserveChannelsContext
422  *
423  * Called by #TpBaseClientClassObserveChannelsImpl when it's done so the D-Bus
424  * method can return.
425  *
426  * Since: 0.11.5
427  */
428 void
tp_observe_channels_context_accept(TpObserveChannelsContext * self)429 tp_observe_channels_context_accept (TpObserveChannelsContext *self)
430 {
431   g_return_if_fail (self->priv->state == TP_OBSERVE_CHANNELS_CONTEXT_STATE_NONE
432       || self->priv->state == TP_OBSERVE_CHANNELS_CONTEXT_STATE_DELAYED);
433   g_return_if_fail (self->priv->dbus_context != NULL);
434 
435   self->priv->state = TP_OBSERVE_CHANNELS_CONTEXT_STATE_DONE;
436   dbus_g_method_return (self->priv->dbus_context);
437 
438   self->priv->dbus_context = NULL;
439 }
440 
441 /**
442  * tp_observe_channels_context_fail:
443  * @self: a #TpObserveChannelsContext
444  * @error: the error to return from the method
445  *
446  * Called by #TpBaseClientClassObserveChannelsImpl to raise a D-Bus error.
447  *
448  * Since: 0.11.5
449  */
450 void
tp_observe_channels_context_fail(TpObserveChannelsContext * self,const GError * error)451 tp_observe_channels_context_fail (TpObserveChannelsContext *self,
452     const GError *error)
453 {
454   g_return_if_fail (self->priv->state == TP_OBSERVE_CHANNELS_CONTEXT_STATE_NONE
455       || self->priv->state == TP_OBSERVE_CHANNELS_CONTEXT_STATE_DELAYED);
456   g_return_if_fail (self->priv->dbus_context != NULL);
457 
458   self->priv->state = TP_OBSERVE_CHANNELS_CONTEXT_STATE_FAILED;
459   dbus_g_method_return_error (self->priv->dbus_context, error);
460 
461   self->priv->dbus_context = NULL;
462 }
463 
464 /**
465  * tp_observe_channels_context_delay:
466  * @self: a #TpObserveChannelsContext
467  *
468  * Called by #TpBaseClientClassObserveChannelsImpl to indicate that it
469  * implements the method in an async way. The caller must take a reference
470  * to the #TpObserveChannelsContext before calling this function, and
471  * is responsible for calling either tp_observe_channels_context_accept() or
472  * tp_observe_channels_context_fail() later.
473  *
474  * Since: 0.11.5
475  */
476 void
tp_observe_channels_context_delay(TpObserveChannelsContext * self)477 tp_observe_channels_context_delay (TpObserveChannelsContext *self)
478 {
479   g_return_if_fail (self->priv->state ==
480       TP_OBSERVE_CHANNELS_CONTEXT_STATE_NONE);
481 
482   self->priv->state = TP_OBSERVE_CHANNELS_CONTEXT_STATE_DELAYED;
483 }
484 
485 /**
486  * tp_observe_channels_context_is_recovering:
487  * @self: a #TpObserveChannelsContext
488  *
489  * If this call to ObserveChannels is for channels that already
490  * existed before this observer started (because the observer used
491  * tp_base_client_set_observer_recover()), return %TRUE.
492  *
493  * In most cases, the result is %FALSE.
494  *
495  * Returns: %TRUE for pre-existing channels, %FALSE for new channels
496  *
497  * Since: 0.11.5
498  */
499 gboolean
tp_observe_channels_context_is_recovering(TpObserveChannelsContext * self)500 tp_observe_channels_context_is_recovering (TpObserveChannelsContext *self)
501 {
502   /* tp_asv_get_boolean returns FALSE if the key is not set which is what we
503    * want */
504   return tp_asv_get_boolean (self->observer_info, "recovering", NULL);
505 }
506 
507 TpObserveChannelsContextState
_tp_observe_channels_context_get_state(TpObserveChannelsContext * self)508 _tp_observe_channels_context_get_state (
509     TpObserveChannelsContext *self)
510 {
511   return self->priv->state;
512 }
513 
514 static gboolean
context_is_prepared(TpObserveChannelsContext * self)515 context_is_prepared (TpObserveChannelsContext *self)
516 {
517   return self->priv->num_pending == 0;
518 }
519 
520 static void
context_check_prepare(TpObserveChannelsContext * self)521 context_check_prepare (TpObserveChannelsContext *self)
522 {
523   if (!context_is_prepared (self))
524     return;
525 
526   /* Context is prepared */
527   g_simple_async_result_complete (self->priv->result);
528 
529   g_object_unref (self->priv->result);
530   self->priv->result = NULL;
531 }
532 
533 static void
cdo_prepare_cb(GObject * source,GAsyncResult * result,gpointer user_data)534 cdo_prepare_cb (GObject *source,
535     GAsyncResult *result,
536     gpointer user_data)
537 {
538   TpObserveChannelsContext *self = user_data;
539   GError *error = NULL;
540 
541   if (self->priv->result == NULL)
542     goto out;
543 
544   if (!tp_proxy_prepare_finish (source, result, &error))
545     {
546       DEBUG ("Failed to prepare ChannelDispatchOperation: %s", error->message);
547 
548       g_error_free (error);
549     }
550 
551   self->priv->num_pending--;
552   context_check_prepare (self);
553 
554 out:
555   g_object_unref (self);
556 }
557 
558 static void
account_prepare_cb(GObject * source,GAsyncResult * result,gpointer user_data)559 account_prepare_cb (GObject *source,
560     GAsyncResult *result,
561     gpointer user_data)
562 {
563   TpObserveChannelsContext *self = user_data;
564   GError *error = NULL;
565 
566   if (self->priv->result == NULL)
567     goto out;
568 
569   if (!tp_proxy_prepare_finish (source, result, &error))
570     {
571       DEBUG ("Failed to prepare account: %s", error->message);
572       g_error_free (error);
573     }
574 
575   self->priv->num_pending--;
576   context_check_prepare (self);
577 
578 out:
579   g_object_unref (self);
580 }
581 
582 static void
conn_prepare_cb(GObject * source,GAsyncResult * result,gpointer user_data)583 conn_prepare_cb (GObject *source,
584     GAsyncResult *result,
585     gpointer user_data)
586 {
587   TpObserveChannelsContext *self = user_data;
588   GError *error = NULL;
589 
590   if (self->priv->result == NULL)
591     goto out;
592 
593   if (!tp_proxy_prepare_finish (source, result, &error))
594     {
595       DEBUG ("Failed to prepare connection: %s", error->message);
596       g_error_free (error);
597     }
598 
599   self->priv->num_pending--;
600   context_check_prepare (self);
601 
602 out:
603   g_object_unref (self);
604 }
605 
606 static void
occ_channel_prepare_cb(GObject * source,GAsyncResult * result,gpointer user_data)607 occ_channel_prepare_cb (GObject *source,
608     GAsyncResult *result,
609     gpointer user_data)
610 {
611   TpObserveChannelsContext *self = user_data;
612   GError *error = NULL;
613 
614   if (self->priv->result == NULL)
615     goto out;
616 
617   if (!tp_proxy_prepare_finish (source, result, &error))
618     {
619       DEBUG ("Failed to prepare channel: %s", error->message);
620       g_error_free (error);
621     }
622 
623   self->priv->num_pending--;
624   context_check_prepare (self);
625 
626 out:
627   g_object_unref (self);
628 }
629 
630 static void
context_prepare(TpObserveChannelsContext * self,const GQuark * account_features,const GQuark * connection_features,const GQuark * channel_features)631 context_prepare (TpObserveChannelsContext *self,
632     const GQuark *account_features,
633     const GQuark *connection_features,
634     const GQuark *channel_features)
635 {
636   GQuark cdo_features[] = { TP_CHANNEL_DISPATCH_OPERATION_FEATURE_CORE, 0 };
637   guint i;
638 
639   self->priv->num_pending = 2;
640 
641   tp_proxy_prepare_async (self->account, account_features,
642       account_prepare_cb, g_object_ref (self));
643 
644   tp_proxy_prepare_async (self->connection, connection_features,
645       conn_prepare_cb, g_object_ref (self));
646 
647   if (self->dispatch_operation != NULL)
648     {
649       self->priv->num_pending++;
650       tp_proxy_prepare_async (self->dispatch_operation, cdo_features,
651           cdo_prepare_cb, g_object_ref (self));
652     }
653 
654   for (i = 0; i < self->channels->len; i++)
655     {
656       TpChannel *channel = g_ptr_array_index (self->channels, i);
657 
658       self->priv->num_pending++;
659 
660       tp_proxy_prepare_async (channel, channel_features,
661           occ_channel_prepare_cb, g_object_ref (self));
662     }
663 }
664 
665 void
_tp_observe_channels_context_prepare_async(TpObserveChannelsContext * self,const GQuark * account_features,const GQuark * connection_features,const GQuark * channel_features,GAsyncReadyCallback callback,gpointer user_data)666 _tp_observe_channels_context_prepare_async (TpObserveChannelsContext *self,
667     const GQuark *account_features,
668     const GQuark *connection_features,
669     const GQuark *channel_features,
670     GAsyncReadyCallback callback,
671     gpointer user_data)
672 {
673   g_return_if_fail (TP_IS_OBSERVE_CHANNELS_CONTEXT (self));
674   /* This is only used once, by TpBaseClient, so for simplicity, we only
675    * allow one asynchronous preparation */
676   g_return_if_fail (self->priv->result == NULL);
677 
678   self->priv->result = g_simple_async_result_new (G_OBJECT (self),
679       callback, user_data, _tp_observe_channels_context_prepare_async);
680 
681   context_prepare (self, account_features, connection_features,
682       channel_features);
683 }
684 
685 gboolean
_tp_observe_channels_context_prepare_finish(TpObserveChannelsContext * self,GAsyncResult * result,GError ** error)686 _tp_observe_channels_context_prepare_finish (
687     TpObserveChannelsContext *self,
688     GAsyncResult *result,
689     GError **error)
690 {
691   _tp_implement_finish_void (self, _tp_observe_channels_context_prepare_async);
692 }
693 
694 /**
695  * tp_observe_channels_context_get_requests:
696  * @self: a #TpObserveChannelsContext
697  *
698  * Return a list of the #TpChannelRequest which have been satisfied by the
699  * channels associated with #self.
700  *
701  * Returns: (transfer full) (element-type TelepathyGLib.ChannelRequest):
702  *  a newly allocated #GList of reffed #TpChannelRequest.
703  *
704  * Since: 0.13.14
705  */
706 GList *
tp_observe_channels_context_get_requests(TpObserveChannelsContext * self)707 tp_observe_channels_context_get_requests (TpObserveChannelsContext *self)
708 {
709   GHashTable *request_props;
710 
711   request_props = tp_asv_get_boxed (self->observer_info, "request-properties",
712       TP_HASH_TYPE_OBJECT_IMMUTABLE_PROPERTIES_MAP);
713   if (request_props == NULL)
714     return NULL;
715 
716   return _tp_create_channel_request_list (
717       tp_proxy_get_factory (self->account), request_props);
718 }
719