1 /*
2  * A factory for TpContacts and plain subclasses of TpProxy
3  *
4  * Copyright © 2011 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:simple-client-factory
23  * @title: TpSimpleClientFactory
24  * @short_description: a factory for #TpContact<!-- -->s and plain subclasses
25  *  of #TpProxy
26  * @see_also: #TpAutomaticClientFactory
27  *
28  * This factory constructs various #TpProxy subclasses as well as #TpContact,
29  * which guarantees that at most one instance of those objects will exist for a
30  * given remote object or contact. It also stores the desired features for
31  * contacts and each type of proxy.
32  *
33  * Note that the factory will not prepare the desired features: it is the
34  * caller's responsibility to do so. By default, only core features are
35  * requested.
36  *
37  * Currently supported classes are #TpAccount, #TpConnection,
38  * #TpChannel and #TpContact. Those objects should always be acquired through a
39  * factory or a "larger" object (e.g. getting the #TpConnection from
40  * a #TpAccount), rather than being constructed directly.
41  *
42  * One can subclass #TpSimpleClientFactory and override some of its virtual
43  * methods to construct more specialized objects. See #TpAutomaticClientFactory
44  * for a subclass which automatically constructs subclasses of #TpChannel for
45  * common channel types.
46  *
47  * An application using its own factory subclass would look like this:
48  * |[
49  * int main(int argc, char *argv[])
50  * {
51  *   TpSimpleClientFactory *factory;
52  *   TpAccountManager *manager;
53  *
54  *   factory = my_factory_new ();
55  *   manager = tp_account_manager_new_with_factory (factory);
56  *   tp_account_manager_set_default (manager);
57  *
58  *   ...
59  *   tp_proxy_prepare_async (manager, am_features, callback, user_data);
60  *   ...
61  * }
62  * ]|
63  *
64  * The call to tp_account_manager_set_default() near the beginning of main()
65  * will ensure that any libraries or plugins which also use Telepathy (and call
66  * tp_account_manager_dup()) will share your #TpAccountManager.
67  *
68  * Since: 0.15.5
69  */
70 
71 /**
72  * TpSimpleClientFactory:
73  *
74  * Data structure representing a #TpSimpleClientFactory
75  *
76  * Since: 0.15.5
77  */
78 
79 /**
80  * TpSimpleClientFactoryClass:
81  * @parent_class: the parent
82  * @create_account: create a #TpAccount;
83  *  see tp_simple_client_factory_ensure_account()
84  * @dup_account_features: implementation of tp_simple_client_factory_dup_account_features()
85  * @create_connection: create a #TpConnection;
86  *  see tp_simple_client_factory_ensure_connection()
87  * @dup_connection_features: implementation of
88  *  tp_simple_client_factory_dup_connection_features()
89  * @create_channel: create a #TpChannel;
90  *  see tp_simple_client_factory_ensure_channel()
91  * @dup_channel_features: implementation of tp_simple_client_factory_dup_channel_features()
92  * @create_contact: create a #TpContact;
93  *  see tp_simple_client_factory_ensure_contact()
94  * @dup_contact_features: implementation of tp_simple_client_factory_dup_contact_features()
95  *
96  * The class structure for #TpSimpleClientFactory.
97  *
98  * #TpSimpleClientFactory maintains a cache of previously-constructed proxy
99  * objects, so the implementations of @create_account,
100  * @create_connection, @create_channel, and @create_contact may assume that a
101  * new object should be created when they are called. The default
102  * implementations create unadorned instances of the relevant classes;
103  * subclasses of the factory may choose to create more interesting proxy
104  * subclasses.
105  *
106  * The default implementation of @dup_channel_features returns
107  * #TP_CHANNEL_FEATURE_CORE, plus all features passed to
108  * tp_simple_client_factory_add_channel_features() by the application.
109  * Subclasses may override this method to prepare more interesting features
110  * from subclasses of #TpChannel, for instance. The default implementations of
111  * the other <function>dup_x_features</function> methods behave similarly.
112  *
113  * Since: 0.15.5
114  */
115 
116 #include "config.h"
117 
118 #include "telepathy-glib/simple-client-factory.h"
119 
120 #include <telepathy-glib/util.h>
121 
122 #define DEBUG_FLAG TP_DEBUG_CLIENT
123 #include "telepathy-glib/connection-internal.h"
124 #include "telepathy-glib/contact-internal.h"
125 #include "telepathy-glib/debug-internal.h"
126 #include "telepathy-glib/simple-client-factory-internal.h"
127 #include "telepathy-glib/util-internal.h"
128 
129 struct _TpSimpleClientFactoryPrivate
130 {
131   TpDBusDaemon *dbus;
132   /* Owned object-path -> weakref to TpProxy */
133   GHashTable *proxy_cache;
134   GArray *desired_account_features;
135   GArray *desired_connection_features;
136   GArray *desired_channel_features;
137   GArray *desired_contact_features;
138 };
139 
140 enum
141 {
142   PROP_DBUS_DAEMON = 1,
143   N_PROPS
144 };
145 
G_DEFINE_TYPE(TpSimpleClientFactory,tp_simple_client_factory,G_TYPE_OBJECT)146 G_DEFINE_TYPE (TpSimpleClientFactory, tp_simple_client_factory, G_TYPE_OBJECT)
147 
148 static void
149 proxy_invalidated_cb (TpProxy *proxy,
150     guint domain,
151     gint code,
152     gchar *message,
153     TpSimpleClientFactory *self)
154 {
155   g_hash_table_remove (self->priv->proxy_cache,
156       tp_proxy_get_object_path (proxy));
157 }
158 
159 static void
insert_proxy(TpSimpleClientFactory * self,gpointer proxy)160 insert_proxy (TpSimpleClientFactory *self,
161     gpointer proxy)
162 {
163   if (proxy == NULL)
164     return;
165 
166   g_hash_table_insert (self->priv->proxy_cache,
167       (gpointer) tp_proxy_get_object_path (proxy), proxy);
168 
169   /* This assume that invalidated signal is emitted from TpProxy dispose. May
170    * change in a future API break? */
171   tp_g_signal_connect_object (proxy, "invalidated",
172       G_CALLBACK (proxy_invalidated_cb), self, 0);
173 }
174 
175 static gpointer
lookup_proxy(TpSimpleClientFactory * self,const gchar * object_path)176 lookup_proxy (TpSimpleClientFactory *self,
177     const gchar *object_path)
178 {
179   return g_hash_table_lookup (self->priv->proxy_cache, object_path);
180 }
181 
182 void
_tp_simple_client_factory_insert_proxy(TpSimpleClientFactory * self,gpointer proxy)183 _tp_simple_client_factory_insert_proxy (TpSimpleClientFactory *self,
184     gpointer proxy)
185 {
186   g_return_if_fail (lookup_proxy (self,
187       tp_proxy_get_object_path (proxy)) == NULL);
188 
189   insert_proxy (self, proxy);
190 }
191 
192 static TpAccount *
create_account_impl(TpSimpleClientFactory * self,const gchar * object_path,const GHashTable * immutable_properties G_GNUC_UNUSED,GError ** error)193 create_account_impl (TpSimpleClientFactory *self,
194     const gchar *object_path,
195     const GHashTable *immutable_properties G_GNUC_UNUSED,
196     GError **error)
197 {
198   return _tp_account_new_with_factory (self, self->priv->dbus, object_path,
199       error);
200 }
201 
202 static GArray *
dup_account_features_impl(TpSimpleClientFactory * self,TpAccount * account)203 dup_account_features_impl (TpSimpleClientFactory *self,
204     TpAccount *account)
205 {
206   return _tp_quark_array_copy (
207       (GQuark *) self->priv->desired_account_features->data);
208 }
209 
210 static TpConnection *
create_connection_impl(TpSimpleClientFactory * self,const gchar * object_path,const GHashTable * immutable_properties G_GNUC_UNUSED,GError ** error)211 create_connection_impl (TpSimpleClientFactory *self,
212     const gchar *object_path,
213     const GHashTable *immutable_properties G_GNUC_UNUSED,
214     GError **error)
215 {
216   return _tp_connection_new_with_factory (self, self->priv->dbus, NULL,
217       object_path, error);
218 }
219 
220 static GArray *
dup_connection_features_impl(TpSimpleClientFactory * self,TpConnection * connection)221 dup_connection_features_impl (TpSimpleClientFactory *self,
222     TpConnection *connection)
223 {
224   return _tp_quark_array_copy (
225       (GQuark *) self->priv->desired_connection_features->data);
226 }
227 
228 static TpChannel *
create_channel_impl(TpSimpleClientFactory * self,TpConnection * conn,const gchar * object_path,const GHashTable * immutable_properties,GError ** error)229 create_channel_impl (TpSimpleClientFactory *self,
230     TpConnection *conn,
231     const gchar *object_path,
232     const GHashTable *immutable_properties,
233     GError **error)
234 {
235   return _tp_channel_new_with_factory (self, conn, object_path,
236       immutable_properties, error);
237 }
238 
239 static GArray *
dup_channel_features_impl(TpSimpleClientFactory * self,TpChannel * channel)240 dup_channel_features_impl (TpSimpleClientFactory *self,
241     TpChannel *channel)
242 {
243   return _tp_quark_array_copy (
244       (GQuark *) self->priv->desired_channel_features->data);
245 }
246 
247 static TpContact *
create_contact_impl(TpSimpleClientFactory * self,TpConnection * connection,TpHandle handle,const gchar * identifier)248 create_contact_impl (TpSimpleClientFactory *self,
249     TpConnection *connection,
250     TpHandle handle,
251     const gchar *identifier)
252 {
253   return _tp_contact_new (connection, handle, identifier);
254 }
255 
256 static GArray *
dup_contact_features_impl(TpSimpleClientFactory * self,TpConnection * connection)257 dup_contact_features_impl (TpSimpleClientFactory *self,
258     TpConnection *connection)
259 {
260   GArray *array;
261 
262   array = g_array_sized_new (FALSE, FALSE, sizeof (TpContactFeature),
263       self->priv->desired_contact_features->len);
264   g_array_append_vals (array, self->priv->desired_contact_features->data,
265       self->priv->desired_contact_features->len);
266 
267   return array;
268 }
269 
270 static void
tp_simple_client_factory_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)271 tp_simple_client_factory_get_property (GObject *object,
272     guint property_id,
273     GValue *value,
274     GParamSpec *pspec)
275 {
276   TpSimpleClientFactory *self = (TpSimpleClientFactory *) object;
277 
278   switch (property_id)
279     {
280     case PROP_DBUS_DAEMON:
281       g_value_set_object (value, self->priv->dbus);
282       break;
283     default:
284       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
285       break;
286   }
287 }
288 
289 static void
tp_simple_client_factory_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)290 tp_simple_client_factory_set_property (GObject *object,
291     guint property_id,
292     const GValue *value,
293     GParamSpec *pspec)
294 {
295   TpSimpleClientFactory *self = (TpSimpleClientFactory *) object;
296 
297   switch (property_id)
298     {
299     case PROP_DBUS_DAEMON:
300       g_assert (self->priv->dbus == NULL); /* construct only */
301       self->priv->dbus = g_value_dup_object (value);
302       break;
303     default:
304       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
305       break;
306   }
307 }
308 
309 static void
tp_simple_client_factory_constructed(GObject * object)310 tp_simple_client_factory_constructed (GObject *object)
311 {
312   TpSimpleClientFactory *self = (TpSimpleClientFactory *) object;
313 
314   if (self->priv->dbus == NULL)
315     self->priv->dbus = tp_dbus_daemon_dup (NULL);
316 
317   G_OBJECT_CLASS (tp_simple_client_factory_parent_class)->constructed (object);
318 }
319 
320 static void
tp_simple_client_factory_finalize(GObject * object)321 tp_simple_client_factory_finalize (GObject *object)
322 {
323   TpSimpleClientFactory *self = (TpSimpleClientFactory *) object;
324 
325   g_clear_object (&self->priv->dbus);
326   tp_clear_pointer (&self->priv->proxy_cache, g_hash_table_unref);
327   tp_clear_pointer (&self->priv->desired_account_features, g_array_unref);
328   tp_clear_pointer (&self->priv->desired_connection_features, g_array_unref);
329   tp_clear_pointer (&self->priv->desired_channel_features, g_array_unref);
330   tp_clear_pointer (&self->priv->desired_contact_features, g_array_unref);
331 
332   G_OBJECT_CLASS (tp_simple_client_factory_parent_class)->finalize (object);
333 }
334 
335 static void
tp_simple_client_factory_init(TpSimpleClientFactory * self)336 tp_simple_client_factory_init (TpSimpleClientFactory *self)
337 {
338   GQuark feature;
339 
340   self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, TP_TYPE_SIMPLE_CLIENT_FACTORY,
341       TpSimpleClientFactoryPrivate);
342 
343   self->priv->proxy_cache = g_hash_table_new (g_str_hash, g_str_equal);
344 
345   self->priv->desired_account_features = g_array_new (TRUE, FALSE,
346       sizeof (GQuark));
347   feature = TP_ACCOUNT_FEATURE_CORE;
348   g_array_append_val (self->priv->desired_account_features, feature);
349 
350   self->priv->desired_connection_features = g_array_new (TRUE, FALSE,
351       sizeof (GQuark));
352   feature = TP_CONNECTION_FEATURE_CORE;
353   g_array_append_val (self->priv->desired_connection_features, feature);
354 
355   self->priv->desired_channel_features = g_array_new (TRUE, FALSE,
356       sizeof (GQuark));
357   feature = TP_CHANNEL_FEATURE_CORE;
358   g_array_append_val (self->priv->desired_channel_features, feature);
359 
360   self->priv->desired_contact_features = g_array_new (FALSE, FALSE,
361       sizeof (TpContactFeature));
362 }
363 
364 static void
tp_simple_client_factory_class_init(TpSimpleClientFactoryClass * klass)365 tp_simple_client_factory_class_init (TpSimpleClientFactoryClass *klass)
366 {
367   GObjectClass *object_class = (GObjectClass *) klass;
368   GParamSpec *param_spec;
369 
370   g_type_class_add_private (klass, sizeof (TpSimpleClientFactoryPrivate));
371 
372   object_class->get_property = tp_simple_client_factory_get_property;
373   object_class->set_property = tp_simple_client_factory_set_property;
374   object_class->constructed = tp_simple_client_factory_constructed;
375   object_class->finalize = tp_simple_client_factory_finalize;
376 
377   klass->create_account = create_account_impl;
378   klass->dup_account_features = dup_account_features_impl;
379   klass->create_connection = create_connection_impl;
380   klass->dup_connection_features = dup_connection_features_impl;
381   klass->create_channel = create_channel_impl;
382   klass->dup_channel_features = dup_channel_features_impl;
383   klass->create_contact = create_contact_impl;
384   klass->dup_contact_features = dup_contact_features_impl;
385 
386   /**
387    * TpSimpleClientFactory:dbus-daemon:
388    *
389    * The D-Bus daemon for this object.
390    */
391   param_spec = g_param_spec_object ("dbus-daemon", "D-Bus daemon",
392       "The D-Bus daemon used by this object",
393       TP_TYPE_DBUS_DAEMON,
394       G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
395   g_object_class_install_property (object_class, PROP_DBUS_DAEMON,
396       param_spec);
397 }
398 
399 /**
400  * tp_simple_client_factory_new:
401  * @dbus: (allow-none): a #TpDBusDaemon, or %NULL
402  *
403  * Creates a new #TpSimpleClientFactory instance. If @dbus is %NULL,
404  * tp_dbus_daemon_dup() will be used.
405  *
406  * Returns: a new #TpSimpleClientFactory
407  *
408  * Since: 0.15.5
409  */
410 TpSimpleClientFactory *
tp_simple_client_factory_new(TpDBusDaemon * dbus)411 tp_simple_client_factory_new (TpDBusDaemon *dbus)
412 {
413   return g_object_new (TP_TYPE_SIMPLE_CLIENT_FACTORY,
414       "dbus-daemon", dbus,
415       NULL);
416 }
417 
418 /**
419  * tp_simple_client_factory_get_dbus_daemon:
420  * @self: a #TpSimpleClientFactory object
421  *
422  * <!-- -->
423  *
424  * Returns: (transfer none): the value of the #TpSimpleClientFactory:dbus-daemon
425  *  property
426  *
427  * Since: 0.15.5
428  */
429 TpDBusDaemon *
tp_simple_client_factory_get_dbus_daemon(TpSimpleClientFactory * self)430 tp_simple_client_factory_get_dbus_daemon (TpSimpleClientFactory *self)
431 {
432   g_return_val_if_fail (TP_IS_SIMPLE_CLIENT_FACTORY (self), NULL);
433 
434   return self->priv->dbus;
435 }
436 
437 /**
438  * tp_simple_client_factory_ensure_account:
439  * @self: a #TpSimpleClientFactory object
440  * @object_path: the object path of an account
441  * @immutable_properties: (transfer none) (element-type utf8 GObject.Value):
442  *  the immutable properties of the account, or %NULL.
443  * @error: Used to raise an error if @object_path is not valid
444  *
445  * Returns a #TpAccount proxy for the account at @object_path. The returned
446  * #TpAccount is cached; the same #TpAccount object will be returned by this
447  * function repeatedly, as long as at least one reference exists.
448  *
449  * Note that the returned #TpAccount is not guaranteed to be ready; the caller
450  * is responsible for calling tp_proxy_prepare_async() with the desired
451  * features (as given by tp_simple_client_factory_dup_account_features()).
452  *
453  * This function is rather low-level. tp_account_manager_dup_valid_accounts()
454  * and #TpAccountManager::validity-changed are more appropriate for most
455  * applications.
456  *
457  * Returns: (transfer full): a reference to a #TpAccount;
458  *  see tp_account_new().
459  *
460  * Since: 0.15.5
461  */
462 TpAccount *
tp_simple_client_factory_ensure_account(TpSimpleClientFactory * self,const gchar * object_path,const GHashTable * immutable_properties,GError ** error)463 tp_simple_client_factory_ensure_account (TpSimpleClientFactory *self,
464     const gchar *object_path,
465     const GHashTable *immutable_properties,
466     GError **error)
467 {
468   TpAccount *account;
469 
470   g_return_val_if_fail (TP_IS_SIMPLE_CLIENT_FACTORY (self), NULL);
471   g_return_val_if_fail (g_variant_is_object_path (object_path), NULL);
472 
473   account = lookup_proxy (self, object_path);
474   if (account != NULL)
475     return g_object_ref (account);
476 
477   account = TP_SIMPLE_CLIENT_FACTORY_GET_CLASS (self)->create_account (self,
478       object_path, immutable_properties, error);
479   insert_proxy (self, account);
480 
481   return account;
482 }
483 
484 /**
485  * tp_simple_client_factory_dup_account_features:
486  * @self: a #TpSimpleClientFactory object
487  * @account: a #TpAccount
488  *
489  * Return a zero-terminated #GArray containing the #TpAccount features that
490  * should be prepared on @account.
491  *
492  * Returns: (transfer full) (element-type GLib.Quark): a newly allocated
493  *  #GArray
494  *
495  * Since: 0.15.5
496  */
497 GArray *
tp_simple_client_factory_dup_account_features(TpSimpleClientFactory * self,TpAccount * account)498 tp_simple_client_factory_dup_account_features (TpSimpleClientFactory *self,
499     TpAccount *account)
500 {
501   g_return_val_if_fail (TP_IS_SIMPLE_CLIENT_FACTORY (self), NULL);
502   g_return_val_if_fail (TP_IS_ACCOUNT (account), NULL);
503   g_return_val_if_fail (tp_proxy_get_factory (account) == self, NULL);
504 
505   return TP_SIMPLE_CLIENT_FACTORY_GET_CLASS (self)->dup_account_features (self,
506       account);
507 }
508 
509 /**
510  * tp_simple_client_factory_add_account_features:
511  * @self: a #TpSimpleClientFactory object
512  * @features: (transfer none) (array zero-terminated=1) (allow-none): an array
513  *  of desired features, ending with 0; %NULL is equivalent to an array
514  *  containing only 0
515  *
516  * Add @features to the desired features to be prepared on #TpAccount
517  * objects. Those features will be added to the features already returned be
518  * tp_simple_client_factory_dup_account_features().
519  *
520  * It is not necessary to add %TP_ACCOUNT_FEATURE_CORE as it is already
521  * included by default.
522  *
523  * Note that these features will not be added to existing #TpAccount
524  * objects; the user must call tp_proxy_prepare_async() themself.
525  *
526  * Since: 0.15.5
527  */
528 void
tp_simple_client_factory_add_account_features(TpSimpleClientFactory * self,const GQuark * features)529 tp_simple_client_factory_add_account_features (
530     TpSimpleClientFactory *self,
531     const GQuark *features)
532 {
533   g_return_if_fail (TP_IS_SIMPLE_CLIENT_FACTORY (self));
534 
535   _tp_quark_array_merge (self->priv->desired_account_features, features, -1);
536 }
537 
538 /**
539  * tp_simple_client_factory_add_account_features_varargs: (skip)
540  * @self: a #TpSimpleClientFactory
541  * @feature: the first feature
542  * @...: the second and subsequent features, if any, ending with 0
543  *
544  * The same as tp_simple_client_factory_add_account_features(), but with a more
545  * convenient calling convention from C.
546  *
547  * Since: 0.15.5
548  */
549 void
tp_simple_client_factory_add_account_features_varargs(TpSimpleClientFactory * self,GQuark feature,...)550 tp_simple_client_factory_add_account_features_varargs (
551     TpSimpleClientFactory *self,
552     GQuark feature,
553     ...)
554 {
555   va_list var_args;
556 
557   g_return_if_fail (TP_IS_SIMPLE_CLIENT_FACTORY (self));
558 
559   va_start (var_args, feature);
560   _tp_quark_array_merge_valist (self->priv->desired_account_features, feature,
561       var_args);
562   va_end (var_args);
563 }
564 
565 /**
566  * tp_simple_client_factory_ensure_connection:
567  * @self: a #TpSimpleClientFactory object
568  * @object_path: the object path of a connection
569  * @immutable_properties: (transfer none) (element-type utf8 GObject.Value):
570  *  the immutable properties of the connection.
571  * @error: Used to raise an error if @object_path is not valid
572  *
573  * Returns a #TpConnection proxy for the connection at @object_path.
574  * The returned #TpConnection is cached; the same #TpConnection object
575  * will be returned by this function repeatedly, as long as at least one
576  * reference exists.
577  *
578  * Note that the returned #TpConnection is not guaranteed to be ready; the
579  * caller is responsible for calling tp_proxy_prepare_async() with the desired
580  * features (as given by tp_simple_client_factory_dup_connection_features()).
581  *
582  * This function is rather low-level. #TpAccount:connection is more
583  * appropriate for most applications.
584  *
585  * Returns: (transfer full): a reference to a #TpConnection;
586  *  see tp_connection_new().
587  *
588  * Since: 0.15.5
589  */
590 TpConnection *
tp_simple_client_factory_ensure_connection(TpSimpleClientFactory * self,const gchar * object_path,const GHashTable * immutable_properties,GError ** error)591 tp_simple_client_factory_ensure_connection (TpSimpleClientFactory *self,
592     const gchar *object_path,
593     const GHashTable *immutable_properties,
594     GError **error)
595 {
596   TpConnection *connection;
597 
598   g_return_val_if_fail (TP_IS_SIMPLE_CLIENT_FACTORY (self), NULL);
599   g_return_val_if_fail (g_variant_is_object_path (object_path), NULL);
600 
601   connection = lookup_proxy (self, object_path);
602   if (connection != NULL)
603     return g_object_ref (connection);
604 
605   connection = TP_SIMPLE_CLIENT_FACTORY_GET_CLASS (self)->create_connection (
606       self, object_path, immutable_properties, error);
607   insert_proxy (self, connection);
608 
609   return connection;
610 }
611 
612 /**
613  * tp_simple_client_factory_dup_connection_features:
614  * @self: a #TpSimpleClientFactory object
615  * @connection: a #TpConnection
616  *
617  * Return a zero-terminated #GArray containing the #TpConnection features that
618  * should be prepared on @connection.
619  *
620  * Returns: (transfer full) (element-type GLib.Quark): a newly allocated
621  *  #GArray
622  *
623  * Since: 0.15.5
624  */
625 GArray *
tp_simple_client_factory_dup_connection_features(TpSimpleClientFactory * self,TpConnection * connection)626 tp_simple_client_factory_dup_connection_features (TpSimpleClientFactory *self,
627     TpConnection *connection)
628 {
629   g_return_val_if_fail (TP_IS_SIMPLE_CLIENT_FACTORY (self), NULL);
630   g_return_val_if_fail (TP_IS_CONNECTION (connection), NULL);
631   g_return_val_if_fail (tp_proxy_get_factory (connection) == self, NULL);
632 
633   return TP_SIMPLE_CLIENT_FACTORY_GET_CLASS (self)->dup_connection_features (
634       self, connection);
635 }
636 
637 /**
638  * tp_simple_client_factory_add_connection_features:
639  * @self: a #TpSimpleClientFactory object
640  * @features: (transfer none) (array zero-terminated=1) (allow-none): an array
641  *  of desired features, ending with 0; %NULL is equivalent to an array
642  *  containing only 0
643  *
644  * Add @features to the desired features to be prepared on #TpConnection
645  * objects. Those features will be added to the features already returned be
646  * tp_simple_client_factory_dup_connection_features().
647  *
648  * It is not necessary to add %TP_CONNECTION_FEATURE_CORE as it is already
649  * included by default.
650  *
651  * Note that these features will not be added to existing #TpConnection
652  * objects; the user must call tp_proxy_prepare_async() themself.
653  *
654  * Since: 0.15.5
655  */
656 void
tp_simple_client_factory_add_connection_features(TpSimpleClientFactory * self,const GQuark * features)657 tp_simple_client_factory_add_connection_features (
658     TpSimpleClientFactory *self,
659     const GQuark *features)
660 {
661   g_return_if_fail (TP_IS_SIMPLE_CLIENT_FACTORY (self));
662 
663   _tp_quark_array_merge (self->priv->desired_connection_features, features, -1);
664 }
665 
666 /**
667  * tp_simple_client_factory_add_connection_features_varargs: (skip)
668  * @self: a #TpSimpleClientFactory
669  * @feature: the first feature
670  * @...: the second and subsequent features, if any, ending with 0
671  *
672  * The same as tp_simple_client_factory_add_connection_features(), but with a
673  * more convenient calling convention from C.
674  *
675  * Since: 0.15.5
676  */
677 void
tp_simple_client_factory_add_connection_features_varargs(TpSimpleClientFactory * self,GQuark feature,...)678 tp_simple_client_factory_add_connection_features_varargs (
679     TpSimpleClientFactory *self,
680     GQuark feature,
681     ...)
682 {
683   va_list var_args;
684 
685   g_return_if_fail (TP_IS_SIMPLE_CLIENT_FACTORY (self));
686 
687   va_start (var_args, feature);
688   _tp_quark_array_merge_valist (self->priv->desired_connection_features,
689       feature, var_args);
690   va_end (var_args);
691 }
692 
693 /**
694  * tp_simple_client_factory_ensure_channel:
695  * @self: a #TpSimpleClientFactory object
696  * @connection: a #TpConnection whose #TpProxy:factory is this object
697  * @object_path: the object path of a channel on @connection
698  * @immutable_properties: (transfer none) (element-type utf8 GObject.Value):
699  *  the immutable properties of the channel
700  * @error: Used to raise an error if @object_path is not valid
701  *
702  * Returns a #TpChannel proxy for the channel at @object_path on @connection.
703  * The returned #TpChannel is cached; the same #TpChannel object
704  * will be returned by this function repeatedly, as long as at least one
705  * reference exists.
706  *
707  * Note that the returned #TpChannel is not guaranteed to be ready; the
708  * caller is responsible for calling tp_proxy_prepare_async() with the desired
709  * features (as given by tp_simple_client_factory_dup_channel_features()).
710  *
711  * This function is rather low-level.
712  * #TpAccountChannelRequest and #TpBaseClient are more appropriate ways
713  * to obtain channels for most applications.
714  *
715  * Returns: (transfer full): a reference to a #TpChannel;
716  *  see tp_channel_new_from_properties().
717  *
718  * Since: 0.15.5
719  */
720 TpChannel *
tp_simple_client_factory_ensure_channel(TpSimpleClientFactory * self,TpConnection * connection,const gchar * object_path,const GHashTable * immutable_properties,GError ** error)721 tp_simple_client_factory_ensure_channel (TpSimpleClientFactory *self,
722     TpConnection *connection,
723     const gchar *object_path,
724     const GHashTable *immutable_properties,
725     GError **error)
726 {
727   TpChannel *channel;
728 
729   g_return_val_if_fail (TP_IS_SIMPLE_CLIENT_FACTORY (self), NULL);
730   g_return_val_if_fail (TP_IS_CONNECTION (connection), NULL);
731   g_return_val_if_fail (tp_proxy_get_factory (connection) == self, NULL);
732   g_return_val_if_fail (g_variant_is_object_path (object_path), NULL);
733 
734   channel = lookup_proxy (self, object_path);
735   if (channel != NULL)
736     return g_object_ref (channel);
737 
738   channel = TP_SIMPLE_CLIENT_FACTORY_GET_CLASS (self)->create_channel (self,
739       connection, object_path, immutable_properties, error);
740   insert_proxy (self, channel);
741 
742   return channel;
743 }
744 
745 /**
746  * tp_simple_client_factory_dup_channel_features:
747  * @self: a #TpSimpleClientFactory object
748  * @channel: a #TpChannel
749  *
750  * Return a zero-terminated #GArray containing the #TpChannel features that
751  * should be prepared on @channel.
752  *
753  * Returns: (transfer full) (element-type GLib.Quark): a newly allocated
754  *  #GArray
755  *
756  * Since: 0.15.5
757  */
758 GArray *
tp_simple_client_factory_dup_channel_features(TpSimpleClientFactory * self,TpChannel * channel)759 tp_simple_client_factory_dup_channel_features (TpSimpleClientFactory *self,
760     TpChannel *channel)
761 {
762   g_return_val_if_fail (TP_IS_SIMPLE_CLIENT_FACTORY (self), NULL);
763   g_return_val_if_fail (TP_IS_CHANNEL (channel), NULL);
764   g_return_val_if_fail (tp_proxy_get_factory (channel) == self, NULL);
765 
766   return TP_SIMPLE_CLIENT_FACTORY_GET_CLASS (self)->dup_channel_features (
767       self, channel);
768 }
769 
770 /**
771  * tp_simple_client_factory_add_channel_features:
772  * @self: a #TpSimpleClientFactory object
773  * @features: (transfer none) (array zero-terminated=1) (allow-none): an array
774  *  of desired features, ending with 0; %NULL is equivalent to an array
775  *  containing only 0
776  *
777  * Add @features to the desired features to be prepared on #TpChannel
778  * objects. Those features will be added to the features already returned be
779  * tp_simple_client_factory_dup_channel_features().
780  *
781  * It is not necessary to add %TP_CHANNEL_FEATURE_CORE as it is already
782  * included by default.
783  *
784  * Note that these features will not be added to existing #TpChannel
785  * objects; the user must call tp_proxy_prepare_async() themself.
786  *
787  * Since: 0.15.5
788  */
789 void
tp_simple_client_factory_add_channel_features(TpSimpleClientFactory * self,const GQuark * features)790 tp_simple_client_factory_add_channel_features (
791     TpSimpleClientFactory *self,
792     const GQuark *features)
793 {
794   g_return_if_fail (TP_IS_SIMPLE_CLIENT_FACTORY (self));
795 
796   _tp_quark_array_merge (self->priv->desired_channel_features, features, -1);
797 }
798 
799 /**
800  * tp_simple_client_factory_add_channel_features_varargs: (skip)
801  * @self: a #TpSimpleClientFactory
802  * @feature: the first feature
803  * @...: the second and subsequent features, if any, ending with 0
804  *
805  * The same as tp_simple_client_factory_add_channel_features(), but with a
806  * more convenient calling convention from C.
807  *
808  * Since: 0.15.5
809  */
810 void
tp_simple_client_factory_add_channel_features_varargs(TpSimpleClientFactory * self,GQuark feature,...)811 tp_simple_client_factory_add_channel_features_varargs (
812     TpSimpleClientFactory *self,
813     GQuark feature,
814     ...)
815 {
816   va_list var_args;
817 
818   g_return_if_fail (TP_IS_SIMPLE_CLIENT_FACTORY (self));
819 
820   va_start (var_args, feature);
821   _tp_quark_array_merge_valist (self->priv->desired_channel_features,
822       feature, var_args);
823   va_end (var_args);
824 }
825 
826 /**
827  * tp_simple_client_factory_ensure_contact:
828  * @self: a #TpSimpleClientFactory object
829  * @connection: a #TpConnection whose #TpProxy:factory is this object
830  * @handle: a #TpHandle
831  * @identifier: a string representing the contact's identifier
832  *
833  * Returns a #TpContact representing @identifier (and @handle) on @connection.
834  * The returned #TpContact is cached; the same #TpContact object
835  * will be returned by this function repeatedly, as long as at least one
836  * reference exists.
837  *
838  * Note that the returned #TpContact is not guaranteed to be ready; the caller
839  * is responsible for calling tp_connection_upgrade_contacts() with the desired
840  * features (as given by tp_simple_client_factory_dup_contact_features()).
841  *
842  * For this function to work properly, tp_connection_has_immortal_handles()
843  * must return %TRUE for @connection.
844  *
845  * Returns: (transfer full): a reference to a #TpContact.
846  *
847  * Since: 0.15.5
848  */
849 TpContact *
tp_simple_client_factory_ensure_contact(TpSimpleClientFactory * self,TpConnection * connection,TpHandle handle,const gchar * identifier)850 tp_simple_client_factory_ensure_contact (TpSimpleClientFactory *self,
851     TpConnection *connection,
852     TpHandle handle,
853     const gchar *identifier)
854 {
855   TpContact *contact;
856 
857   g_return_val_if_fail (TP_IS_SIMPLE_CLIENT_FACTORY (self), NULL);
858   g_return_val_if_fail (TP_IS_CONNECTION (connection), NULL);
859   g_return_val_if_fail (tp_proxy_get_factory (connection) == self, NULL);
860   g_return_val_if_fail (tp_connection_has_immortal_handles (connection), NULL);
861   g_return_val_if_fail (handle != 0, NULL);
862   g_return_val_if_fail (identifier != NULL, NULL);
863 
864   contact = tp_connection_dup_contact_if_possible (connection,
865       handle, identifier);
866   if (contact != NULL)
867     return contact;
868 
869   contact = TP_SIMPLE_CLIENT_FACTORY_GET_CLASS (self)->create_contact (self,
870       connection, handle, identifier);
871   _tp_connection_add_contact (connection, handle, contact);
872 
873   return contact;
874 }
875 
876 static void
upgrade_contacts_cb(GObject * source,GAsyncResult * result,gpointer user_data)877 upgrade_contacts_cb (GObject *source,
878     GAsyncResult *result,
879     gpointer user_data)
880 {
881   TpConnection *connection = (TpConnection *) source;
882   GSimpleAsyncResult *my_result = user_data;
883   GPtrArray *contacts;
884   GError *error = NULL;
885 
886   if (!tp_connection_upgrade_contacts_finish (connection, result,
887           &contacts, &error))
888     {
889       g_simple_async_result_take_error (my_result, error);
890     }
891   else
892     {
893       g_simple_async_result_set_op_res_gpointer (my_result, contacts,
894           (GDestroyNotify) g_ptr_array_unref);
895     }
896 
897   g_simple_async_result_complete (my_result);
898   g_object_unref (my_result);
899 }
900 
901 
902 /**
903  * tp_simple_client_factory_upgrade_contacts_async:
904  * @self: a #TpSimpleClientFactory object
905  * @connection: a #TpConnection whose #TpProxy:factory is this object
906  * @n_contacts: The number of contacts in @contacts (must be at least 1)
907  * @contacts: (array length=n_contacts): An array of #TpContact objects
908  *  associated with @self
909  * @callback: a callback to call when the operation finishes
910  * @user_data: data to pass to @callback
911  *
912  * Same as tp_connection_upgrade_contacts_async(), but prepare contacts with all
913  * features previously passed to
914  * tp_simple_client_factory_add_contact_features().
915  *
916  * Since: 0.19.1
917  */
918 void
tp_simple_client_factory_upgrade_contacts_async(TpSimpleClientFactory * self,TpConnection * connection,guint n_contacts,TpContact * const * contacts,GAsyncReadyCallback callback,gpointer user_data)919 tp_simple_client_factory_upgrade_contacts_async (
920     TpSimpleClientFactory *self,
921     TpConnection *connection,
922     guint n_contacts,
923     TpContact * const *contacts,
924     GAsyncReadyCallback callback,
925     gpointer user_data)
926 {
927   GSimpleAsyncResult *result;
928   GArray *features;
929 
930   /* no real reason this shouldn't work, but it's really confusing
931    * and probably indicates an error */
932   g_warn_if_fail (tp_proxy_get_factory (connection) == self);
933 
934   result = g_simple_async_result_new ((GObject *) self, callback, user_data,
935       tp_simple_client_factory_upgrade_contacts_async);
936 
937   features = tp_simple_client_factory_dup_contact_features (self, connection);
938   tp_connection_upgrade_contacts_async (connection, n_contacts, contacts,
939       features->len, (TpContactFeature *) features->data,
940       upgrade_contacts_cb, result);
941   g_array_unref (features);
942 }
943 
944 /**
945  * tp_simple_client_factory_upgrade_contacts_finish:
946  * @self: a #TpSimpleClientFactory
947  * @result: a #GAsyncResult
948  * @contacts: (element-type TelepathyGLib.Contact) (transfer container) (out) (allow-none):
949  *  a location to set a #GPtrArray of upgraded #TpContact, or %NULL.
950  * @error: a #GError to fill
951  *
952  * Finishes tp_simple_client_factory_upgrade_contacts_async()
953  *
954  * Returns: %TRUE on success, %FALSE otherwise.
955  * Since: 0.19.1
956  */
957 gboolean
tp_simple_client_factory_upgrade_contacts_finish(TpSimpleClientFactory * self,GAsyncResult * result,GPtrArray ** contacts,GError ** error)958 tp_simple_client_factory_upgrade_contacts_finish (
959     TpSimpleClientFactory *self,
960     GAsyncResult *result,
961     GPtrArray **contacts,
962     GError **error)
963 {
964   _tp_implement_finish_copy_pointer (self,
965       tp_simple_client_factory_upgrade_contacts_async,
966       g_ptr_array_ref, contacts);
967 }
968 
969 static void
dup_contact_by_id_cb(GObject * source,GAsyncResult * result,gpointer user_data)970 dup_contact_by_id_cb (GObject *source,
971     GAsyncResult *result,
972     gpointer user_data)
973 {
974   TpConnection *connection = (TpConnection *) source;
975   GSimpleAsyncResult *my_result = user_data;
976   TpContact *contact;
977   GError *error = NULL;
978 
979   contact = tp_connection_dup_contact_by_id_finish (connection, result, &error);
980   if (contact == NULL)
981     {
982       g_simple_async_result_take_error (my_result, error);
983     }
984   else
985     {
986       g_simple_async_result_set_op_res_gpointer (my_result, contact,
987           g_object_unref);
988     }
989 
990   g_simple_async_result_complete (my_result);
991   g_object_unref (my_result);
992 }
993 
994 
995 /**
996  * tp_simple_client_factory_ensure_contact_by_id_async:
997  * @self: a #TpSimpleClientFactory object
998  * @connection: a #TpConnection
999  * @identifier: a string representing the contact's identifier
1000  * @callback: a callback to call when the operation finishes
1001  * @user_data: data to pass to @callback
1002  *
1003  * Same as tp_connection_dup_contact_by_id_async(), but prepare the
1004  * contact with all features previously passed to
1005  * tp_simple_client_factory_add_contact_features().
1006  *
1007  * Since: 0.19.1
1008  */
1009 void
tp_simple_client_factory_ensure_contact_by_id_async(TpSimpleClientFactory * self,TpConnection * connection,const gchar * identifier,GAsyncReadyCallback callback,gpointer user_data)1010 tp_simple_client_factory_ensure_contact_by_id_async (
1011     TpSimpleClientFactory *self,
1012     TpConnection *connection,
1013     const gchar *identifier,
1014     GAsyncReadyCallback callback,
1015     gpointer user_data)
1016 {
1017   GSimpleAsyncResult *result;
1018   GArray *features;
1019 
1020   result = g_simple_async_result_new ((GObject *) self, callback, user_data,
1021       tp_simple_client_factory_ensure_contact_by_id_async);
1022 
1023   features = tp_simple_client_factory_dup_contact_features (self, connection);
1024   tp_connection_dup_contact_by_id_async (connection, identifier,
1025       features->len, (TpContactFeature *) features->data,
1026       dup_contact_by_id_cb, result);
1027   g_array_unref (features);
1028 }
1029 
1030 /**
1031  * tp_simple_client_factory_ensure_contact_by_id_finish:
1032  * @self: a #TpSimpleClientFactory
1033  * @result: a #GAsyncResult
1034  * @error: a #GError to fill
1035  *
1036  * Finishes tp_simple_client_factory_ensure_contact_by_id_async()
1037  *
1038  * Returns: (transfer full): a #TpContact or %NULL on error.
1039  * Since: 0.19.1
1040  */
1041 TpContact *
tp_simple_client_factory_ensure_contact_by_id_finish(TpSimpleClientFactory * self,GAsyncResult * result,GError ** error)1042 tp_simple_client_factory_ensure_contact_by_id_finish (
1043     TpSimpleClientFactory *self,
1044     GAsyncResult *result,
1045     GError **error)
1046 {
1047   _tp_implement_finish_return_copy_pointer (self,
1048       tp_simple_client_factory_ensure_contact_by_id_async, g_object_ref);
1049 }
1050 
1051 /**
1052  * tp_simple_client_factory_dup_contact_features:
1053  * @self: a #TpSimpleClientFactory object
1054  * @connection: a #TpConnection
1055  *
1056  * Return a #GArray containing the #TpContactFeature that should be prepared on
1057  * all contacts of @connection.
1058  *
1059  * Returns: (transfer full) (element-type TelepathyGLib.ContactFeature): a newly
1060  *  allocated #GArray
1061  *
1062  * Since: 0.15.5
1063  */
1064 GArray *
tp_simple_client_factory_dup_contact_features(TpSimpleClientFactory * self,TpConnection * connection)1065 tp_simple_client_factory_dup_contact_features (TpSimpleClientFactory *self,
1066     TpConnection *connection)
1067 {
1068   g_return_val_if_fail (TP_IS_SIMPLE_CLIENT_FACTORY (self), NULL);
1069   g_return_val_if_fail (TP_IS_CONNECTION (connection), NULL);
1070   g_return_val_if_fail (tp_proxy_get_factory (connection) == self, NULL);
1071 
1072   return TP_SIMPLE_CLIENT_FACTORY_GET_CLASS (self)->dup_contact_features (
1073       self, connection);
1074 }
1075 
1076 /**
1077  * tp_simple_client_factory_add_contact_features:
1078  * @self: a #TpSimpleClientFactory object
1079  * @n_features: The number of features in @features (may be 0)
1080  * @features: (array length=n_features) (allow-none): an array of desired
1081  *  features (may be %NULL if @n_features is 0)
1082  *
1083  * Add @features to the desired features to be prepared on #TpContact
1084  * objects. Those features will be added to the features already returned be
1085  * tp_simple_client_factory_dup_contact_features().
1086  *
1087  * Note that these features will not be added to existing #TpContact
1088  * objects; the user must call tp_connection_upgrade_contacts() themself.
1089  *
1090  * Since: 0.15.5
1091  */
1092 void
tp_simple_client_factory_add_contact_features(TpSimpleClientFactory * self,guint n_features,const TpContactFeature * features)1093 tp_simple_client_factory_add_contact_features (TpSimpleClientFactory *self,
1094     guint n_features, const TpContactFeature *features)
1095 {
1096   guint i;
1097 
1098   g_return_if_fail (TP_IS_SIMPLE_CLIENT_FACTORY (self));
1099 
1100   /* Add features into desired_contact_features avoiding dups */
1101   for (i = 0; i < n_features; i++)
1102     {
1103       guint j;
1104       gboolean found = FALSE;
1105 
1106       for (j = 0; j < self->priv->desired_contact_features->len; j++)
1107         {
1108           if (features[i] == g_array_index (
1109               self->priv->desired_contact_features, TpContactFeature, j))
1110             {
1111               found = TRUE;
1112               break;
1113             }
1114         }
1115 
1116       if (!found)
1117         g_array_append_val (self->priv->desired_contact_features, features[i]);
1118     }
1119 }
1120 
1121 /**
1122  * tp_simple_client_factory_add_contact_features_varargs: (skip)
1123  * @self: a #TpSimpleClientFactory
1124  * @feature: the first feature
1125  * @...: the second and subsequent features, if any, ending with
1126  *  %TP_CONTACT_FEATURE_INVALID
1127  *
1128  * The same as tp_simple_client_factory_add_contact_features(), but with a
1129  * more convenient calling convention from C.
1130  *
1131  * Since: 0.15.5
1132  */
1133 void
tp_simple_client_factory_add_contact_features_varargs(TpSimpleClientFactory * self,TpContactFeature feature,...)1134 tp_simple_client_factory_add_contact_features_varargs (
1135     TpSimpleClientFactory *self,
1136     TpContactFeature feature,
1137     ...)
1138 {
1139   va_list var_args;
1140   GArray *features;
1141   TpContactFeature f;
1142 
1143   g_return_if_fail (TP_IS_SIMPLE_CLIENT_FACTORY (self));
1144 
1145   va_start (var_args, feature);
1146   features = g_array_new (FALSE, FALSE, sizeof (TpContactFeature));
1147 
1148   for (f = feature; f != TP_CONTACT_FEATURE_INVALID;
1149       f = va_arg (var_args, TpContactFeature))
1150     g_array_append_val (features, f);
1151 
1152   tp_simple_client_factory_add_contact_features (self, features->len,
1153       (TpContactFeature *) features->data);
1154 
1155   g_array_unref (features);
1156   va_end (var_args);
1157 }
1158 
1159 /*
1160  * _tp_simple_client_factory_ensure_channel_request:
1161  * @self: a #TpSimpleClientFactory object
1162  * @object_path: the object path of a channel request
1163  * @immutable_properties: (transfer none) (element-type utf8 GObject.Value):
1164  *  the immutable properties of the channel request
1165  * @error: Used to raise an error if @object_path is not valid
1166  *
1167  * Returns a #TpChannelRequest for @object_path. The returned
1168  * #TpChannelRequest is cached; the same #TpChannelRequest object will be
1169  * returned by this function repeatedly, as long as at least one reference
1170  * exists.
1171  *
1172  * Note that the returned #TpChannelRequest is not guaranteed to be ready; the
1173  * caller is responsible for calling tp_proxy_prepare_async().
1174  *
1175  * Returns: (transfer full): a reference to a #TpChannelRequest;
1176  *  see tp_channel_request_new().
1177  *
1178  * Since: 0.15.5
1179  */
1180 TpChannelRequest *
_tp_simple_client_factory_ensure_channel_request(TpSimpleClientFactory * self,const gchar * object_path,GHashTable * immutable_properties,GError ** error)1181 _tp_simple_client_factory_ensure_channel_request (TpSimpleClientFactory *self,
1182     const gchar *object_path,
1183     GHashTable *immutable_properties,
1184     GError **error)
1185 {
1186   TpChannelRequest *request;
1187 
1188   g_return_val_if_fail (TP_IS_SIMPLE_CLIENT_FACTORY (self), NULL);
1189   g_return_val_if_fail (g_variant_is_object_path (object_path), NULL);
1190 
1191   request = lookup_proxy (self, object_path);
1192   if (request != NULL)
1193     {
1194       /* A common usage is request_and_handle, in that case EnsureChannel
1195        * returns only the object-path of the ChannelRequest but not properties.
1196        * The TpChannelRequest will be created with no properties, then when
1197        * handling we get the properties and we reuse the same TpChannelRequest
1198        * object, and we can give it the immutable-properties. */
1199       _tp_channel_request_ensure_immutable_properties (request,
1200           immutable_properties);
1201       return g_object_ref (request);
1202     }
1203 
1204   request = _tp_channel_request_new_with_factory (self, self->priv->dbus,
1205       object_path, immutable_properties, error);
1206   insert_proxy (self, request);
1207 
1208   return request;
1209 }
1210 
1211 /*
1212  * _tp_simple_client_factory_ensure_channel_dispatch_operation:
1213  * @self: a #TpSimpleClientFactory object
1214  * @object_path: the object path of a channel dispatch operation
1215  * @immutable_properties: (transfer none) (element-type utf8 GObject.Value):
1216  *  the immutable properties of the channel dispatch operation
1217  * @error: Used to raise an error if @object_path is not valid
1218  *
1219  * Returns a #TpChannelDispatchOperation for @object_path.
1220  * The returned #TpChannelDispatchOperation is cached; the same
1221  * #TpChannelDispatchOperation object will be returned by this function
1222  * repeatedly, as long as at least one reference exists.
1223  *
1224  * Note that the returned #TpChannelDispatchOperation is not guaranteed to be
1225  * ready; the caller is responsible for calling tp_proxy_prepare_async().
1226  *
1227  * Returns: (transfer full): a reference to a
1228  *  #TpChannelDispatchOperation; see tp_channel_dispatch_operation_new().
1229  *
1230  * Since: 0.15.5
1231  */
1232 TpChannelDispatchOperation *
_tp_simple_client_factory_ensure_channel_dispatch_operation(TpSimpleClientFactory * self,const gchar * object_path,GHashTable * immutable_properties,GError ** error)1233 _tp_simple_client_factory_ensure_channel_dispatch_operation (
1234     TpSimpleClientFactory *self,
1235     const gchar *object_path,
1236     GHashTable *immutable_properties,
1237     GError **error)
1238 {
1239   TpChannelDispatchOperation *dispatch;
1240 
1241   g_return_val_if_fail (TP_IS_SIMPLE_CLIENT_FACTORY (self), NULL);
1242   g_return_val_if_fail (g_variant_is_object_path (object_path), NULL);
1243 
1244   dispatch = lookup_proxy (self, object_path);
1245   if (dispatch != NULL)
1246     return g_object_ref (dispatch);
1247 
1248   dispatch = _tp_channel_dispatch_operation_new_with_factory (self,
1249       self->priv->dbus, object_path, immutable_properties, error);
1250   insert_proxy (self, dispatch);
1251 
1252   return dispatch;
1253 }
1254