GDBus: Catch up with new PropertiesChanged signal
[platform/upstream/glib.git] / gio / gdbusproxy.c
1 /* GDBus - GLib D-Bus Library
2  *
3  * Copyright (C) 2008-2010 Red Hat, Inc.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General
16  * Public License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
18  * Boston, MA 02111-1307, USA.
19  *
20  * Author: David Zeuthen <davidz@redhat.com>
21  */
22
23 #include "config.h"
24
25 #include <stdlib.h>
26 #include <string.h>
27
28 #include <gobject/gvaluecollector.h>
29
30 #include "gdbusutils.h"
31 #include "gdbusproxy.h"
32 #include "gioenumtypes.h"
33 #include "gdbusconnection.h"
34 #include "gdbuserror.h"
35 #include "gdbusprivate.h"
36 #include "gio-marshal.h"
37 #include "ginitable.h"
38 #include "gasyncinitable.h"
39 #include "gioerror.h"
40 #include "gasyncresult.h"
41 #include "gsimpleasyncresult.h"
42
43 #include "glibintl.h"
44 #include "gioalias.h"
45
46 /**
47  * SECTION:gdbusproxy
48  * @short_description: Base class for proxies
49  * @include: gio/gio.h
50  *
51  * #GDBusProxy is a base class used for proxies to access a D-Bus
52  * interface on a remote object. A #GDBusProxy can only be constructed
53  * for unique name bus and does not track whether the name
54  * vanishes. Use g_bus_watch_proxy() to construct #GDBusProxy proxies
55  * for owners of a well-known names.
56  */
57
58 struct _GDBusProxyPrivate
59 {
60   GDBusConnection *connection;
61   GDBusProxyFlags flags;
62   gchar *unique_bus_name;
63   gchar *object_path;
64   gchar *interface_name;
65   gint timeout_msec;
66
67   /* gchar* -> GVariant* */
68   GHashTable *properties;
69
70   GDBusInterfaceInfo *expected_interface;
71
72   guint properties_changed_subscriber_id;
73   guint signals_subscriber_id;
74 };
75
76 enum
77 {
78   PROP_0,
79   PROP_G_CONNECTION,
80   PROP_G_UNIQUE_BUS_NAME,
81   PROP_G_FLAGS,
82   PROP_G_OBJECT_PATH,
83   PROP_G_INTERFACE_NAME,
84   PROP_G_DEFAULT_TIMEOUT,
85   PROP_G_INTERFACE_INFO
86 };
87
88 enum
89 {
90   PROPERTIES_CHANGED_SIGNAL,
91   SIGNAL_SIGNAL,
92   LAST_SIGNAL,
93 };
94
95 static void g_dbus_proxy_constructed (GObject *object);
96
97 guint signals[LAST_SIGNAL] = {0};
98
99 static void initable_iface_init       (GInitableIface *initable_iface);
100 static void async_initable_iface_init (GAsyncInitableIface *async_initable_iface);
101
102 G_DEFINE_TYPE_WITH_CODE (GDBusProxy, g_dbus_proxy, G_TYPE_OBJECT,
103                          G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init)
104                          G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, async_initable_iface_init)
105                          );
106
107 static void
108 g_dbus_proxy_finalize (GObject *object)
109 {
110   GDBusProxy *proxy = G_DBUS_PROXY (object);
111
112   if (proxy->priv->properties_changed_subscriber_id > 0)
113     g_dbus_connection_signal_unsubscribe (proxy->priv->connection,
114                                           proxy->priv->properties_changed_subscriber_id);
115
116   if (proxy->priv->signals_subscriber_id > 0)
117     g_dbus_connection_signal_unsubscribe (proxy->priv->connection,
118                                           proxy->priv->signals_subscriber_id);
119
120   g_object_unref (proxy->priv->connection);
121   g_free (proxy->priv->unique_bus_name);
122   g_free (proxy->priv->object_path);
123   g_free (proxy->priv->interface_name);
124   if (proxy->priv->properties != NULL)
125     g_hash_table_unref (proxy->priv->properties);
126
127   if (proxy->priv->expected_interface != NULL)
128     g_dbus_interface_info_unref (proxy->priv->expected_interface);
129
130   G_OBJECT_CLASS (g_dbus_proxy_parent_class)->finalize (object);
131 }
132
133 static void
134 g_dbus_proxy_get_property (GObject    *object,
135                            guint       prop_id,
136                            GValue     *value,
137                            GParamSpec *pspec)
138 {
139   GDBusProxy *proxy = G_DBUS_PROXY (object);
140
141   switch (prop_id)
142     {
143     case PROP_G_CONNECTION:
144       g_value_set_object (value, proxy->priv->connection);
145       break;
146
147     case PROP_G_FLAGS:
148       g_value_set_flags (value, proxy->priv->flags);
149       break;
150
151     case PROP_G_UNIQUE_BUS_NAME:
152       g_value_set_string (value, proxy->priv->unique_bus_name);
153       break;
154
155     case PROP_G_OBJECT_PATH:
156       g_value_set_string (value, proxy->priv->object_path);
157       break;
158
159     case PROP_G_INTERFACE_NAME:
160       g_value_set_string (value, proxy->priv->interface_name);
161       break;
162
163     case PROP_G_DEFAULT_TIMEOUT:
164       g_value_set_int (value, proxy->priv->timeout_msec);
165       break;
166
167     case PROP_G_INTERFACE_INFO:
168       g_value_set_boxed (value, g_dbus_proxy_get_interface_info (proxy));
169       break;
170
171     default:
172       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
173       break;
174     }
175 }
176
177 static void
178 g_dbus_proxy_set_property (GObject      *object,
179                            guint         prop_id,
180                            const GValue *value,
181                            GParamSpec   *pspec)
182 {
183   GDBusProxy *proxy = G_DBUS_PROXY (object);
184
185   switch (prop_id)
186     {
187     case PROP_G_CONNECTION:
188       proxy->priv->connection = g_value_dup_object (value);
189       break;
190
191     case PROP_G_FLAGS:
192       proxy->priv->flags = g_value_get_flags (value);
193       break;
194
195     case PROP_G_UNIQUE_BUS_NAME:
196       proxy->priv->unique_bus_name = g_value_dup_string (value);
197       break;
198
199     case PROP_G_OBJECT_PATH:
200       proxy->priv->object_path = g_value_dup_string (value);
201       break;
202
203     case PROP_G_INTERFACE_NAME:
204       proxy->priv->interface_name = g_value_dup_string (value);
205       break;
206
207     case PROP_G_DEFAULT_TIMEOUT:
208       g_dbus_proxy_set_default_timeout (proxy, g_value_get_int (value));
209       break;
210
211     case PROP_G_INTERFACE_INFO:
212       g_dbus_proxy_set_interface_info (proxy, g_value_get_boxed (value));
213       break;
214
215     default:
216       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
217       break;
218     }
219 }
220
221 static void
222 g_dbus_proxy_class_init (GDBusProxyClass *klass)
223 {
224   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
225
226   gobject_class->finalize     = g_dbus_proxy_finalize;
227   gobject_class->set_property = g_dbus_proxy_set_property;
228   gobject_class->get_property = g_dbus_proxy_get_property;
229   gobject_class->constructed  = g_dbus_proxy_constructed;
230
231   /* Note that all property names are prefixed to avoid collisions with D-Bus property names
232    * in derived classes */
233
234   /**
235    * GDBusProxy:g-interface-info:
236    *
237    * Ensure that interactions with this proxy conform to the given
238    * interface.  For example, when completing a method call, if the
239    * type signature of the message isn't what's expected, the given
240    * #GError is set.  Signals that have a type signature mismatch are
241    * simply dropped.
242    *
243    * Since: 2.26
244    */
245   g_object_class_install_property (gobject_class,
246                                    PROP_G_INTERFACE_INFO,
247                                    g_param_spec_boxed ("g-interface-info",
248                                                        P_("Interface Information"),
249                                                        P_("Interface Information"),
250                                                        G_TYPE_DBUS_INTERFACE_INFO,
251                                                        G_PARAM_READABLE |
252                                                        G_PARAM_WRITABLE |
253                                                        G_PARAM_STATIC_NAME |
254                                                        G_PARAM_STATIC_BLURB |
255                                                        G_PARAM_STATIC_NICK));
256
257   /**
258    * GDBusProxy:g-connection:
259    *
260    * The #GDBusConnection the proxy is for.
261    *
262    * Since: 2.26
263    */
264   g_object_class_install_property (gobject_class,
265                                    PROP_G_CONNECTION,
266                                    g_param_spec_object ("g-connection",
267                                                         P_("g-connection"),
268                                                         P_("The connection the proxy is for"),
269                                                         G_TYPE_DBUS_CONNECTION,
270                                                         G_PARAM_READABLE |
271                                                         G_PARAM_WRITABLE |
272                                                         G_PARAM_CONSTRUCT_ONLY |
273                                                         G_PARAM_STATIC_NAME |
274                                                         G_PARAM_STATIC_BLURB |
275                                                         G_PARAM_STATIC_NICK));
276
277   /**
278    * GDBusProxy:g-flags:
279    *
280    * Flags from the #GDBusProxyFlags enumeration.
281    *
282    * Since: 2.26
283    */
284   g_object_class_install_property (gobject_class,
285                                    PROP_G_FLAGS,
286                                    g_param_spec_flags ("g-flags",
287                                                        P_("g-flags"),
288                                                        P_("Flags for the proxy"),
289                                                        G_TYPE_DBUS_PROXY_FLAGS,
290                                                        G_DBUS_PROXY_FLAGS_NONE,
291                                                        G_PARAM_READABLE |
292                                                        G_PARAM_WRITABLE |
293                                                        G_PARAM_CONSTRUCT_ONLY |
294                                                        G_PARAM_STATIC_NAME |
295                                                        G_PARAM_STATIC_BLURB |
296                                                        G_PARAM_STATIC_NICK));
297
298   /**
299    * GDBusProxy:g-unique-bus-name:
300    *
301    * The unique bus name the proxy is for.
302    *
303    * Since: 2.26
304    */
305   g_object_class_install_property (gobject_class,
306                                    PROP_G_UNIQUE_BUS_NAME,
307                                    g_param_spec_string ("g-unique-bus-name",
308                                                         P_("g-unique-bus-name"),
309                                                         P_("The unique bus name the proxy is for"),
310                                                         NULL,
311                                                         G_PARAM_READABLE |
312                                                         G_PARAM_WRITABLE |
313                                                         G_PARAM_CONSTRUCT_ONLY |
314                                                         G_PARAM_STATIC_NAME |
315                                                         G_PARAM_STATIC_BLURB |
316                                                         G_PARAM_STATIC_NICK));
317
318   /**
319    * GDBusProxy:g-object-path:
320    *
321    * The object path the proxy is for.
322    *
323    * Since: 2.26
324    */
325   g_object_class_install_property (gobject_class,
326                                    PROP_G_OBJECT_PATH,
327                                    g_param_spec_string ("g-object-path",
328                                                         P_("g-object-path"),
329                                                         P_("The object path the proxy is for"),
330                                                         NULL,
331                                                         G_PARAM_READABLE |
332                                                         G_PARAM_WRITABLE |
333                                                         G_PARAM_CONSTRUCT_ONLY |
334                                                         G_PARAM_STATIC_NAME |
335                                                         G_PARAM_STATIC_BLURB |
336                                                         G_PARAM_STATIC_NICK));
337
338   /**
339    * GDBusProxy:g-interface-name:
340    *
341    * The D-Bus interface name the proxy is for.
342    *
343    * Since: 2.26
344    */
345   g_object_class_install_property (gobject_class,
346                                    PROP_G_INTERFACE_NAME,
347                                    g_param_spec_string ("g-interface-name",
348                                                         P_("g-interface-name"),
349                                                         P_("The D-Bus interface name the proxy is for"),
350                                                         NULL,
351                                                         G_PARAM_READABLE |
352                                                         G_PARAM_WRITABLE |
353                                                         G_PARAM_CONSTRUCT_ONLY |
354                                                         G_PARAM_STATIC_NAME |
355                                                         G_PARAM_STATIC_BLURB |
356                                                         G_PARAM_STATIC_NICK));
357
358   /**
359    * GDBusProxy:g-default-timeout:
360    *
361    * The timeout to use if -1 (specifying default timeout) is passed
362    * as @timeout_msec in the g_dbus_proxy_call() and
363    * g_dbus_proxy_call_sync() functions.
364    *
365    * This allows applications to set a proxy-wide timeout for all
366    * remote method invocations on the proxy. If this property is -1,
367    * the default timeout (typically 25 seconds) is used. If set to
368    * %G_MAXINT, then no timeout is used.
369    *
370    * Since: 2.26
371    */
372   g_object_class_install_property (gobject_class,
373                                    PROP_G_DEFAULT_TIMEOUT,
374                                    g_param_spec_int ("g-default-timeout",
375                                                      P_("Default Timeout"),
376                                                      P_("Timeout for remote method invocation"),
377                                                      -1,
378                                                      G_MAXINT,
379                                                      -1,
380                                                      G_PARAM_READABLE |
381                                                      G_PARAM_WRITABLE |
382                                                      G_PARAM_CONSTRUCT |
383                                                      G_PARAM_STATIC_NAME |
384                                                      G_PARAM_STATIC_BLURB |
385                                                      G_PARAM_STATIC_NICK));
386
387   /**
388    * GDBusProxy::g-properties-changed:
389    * @proxy: The #GDBusProxy emitting the signal.
390    * @changed_properties: A #GVariant containing the properties that
391    * changed or %NULL if no properties changed.
392    * @invalidated_properties: A %NULL terminated list of properties that was
393    * invalidated or %NULL if no properties was invalidated.
394    *
395    * Emitted when one or more D-Bus properties on @proxy changes. The
396    * local cache has already been updated when this signal fires.
397    *
398    * This signal corresponds to the
399    * <literal>PropertiesChanged</literal> D-Bus signal on the
400    * <literal>org.freedesktop.DBus.Properties</literal> interface.
401    *
402    * Since: 2.26
403    */
404   signals[PROPERTIES_CHANGED_SIGNAL] = g_signal_new ("g-properties-changed",
405                                                      G_TYPE_DBUS_PROXY,
406                                                      G_SIGNAL_RUN_LAST,
407                                                      G_STRUCT_OFFSET (GDBusProxyClass, g_properties_changed),
408                                                      NULL,
409                                                      NULL,
410                                                      _gio_marshal_VOID__BOXED_BOXED,
411                                                      G_TYPE_NONE,
412                                                      2,
413                                                      G_TYPE_VARIANT,
414                                                      G_TYPE_STRV);
415
416   /**
417    * GDBusProxy::g-signal:
418    * @proxy: The #GDBusProxy emitting the signal.
419    * @sender_name: The sender of the signal or %NULL if the connection is not a bus connection.
420    * @signal_name: The name of the signal.
421    * @parameters: A #GVariant tuple with parameters for the signal.
422    *
423    * Emitted when a signal from the remote object and interface that @proxy is for, has been received.
424    *
425    * Since: 2.26
426    */
427   signals[SIGNAL_SIGNAL] = g_signal_new ("g-signal",
428                                          G_TYPE_DBUS_PROXY,
429                                          G_SIGNAL_RUN_LAST,
430                                          G_STRUCT_OFFSET (GDBusProxyClass, g_signal),
431                                          NULL,
432                                          NULL,
433                                          _gio_marshal_VOID__STRING_STRING_BOXED,
434                                          G_TYPE_NONE,
435                                          3,
436                                          G_TYPE_STRING,
437                                          G_TYPE_STRING,
438                                          G_TYPE_VARIANT);
439
440
441   g_type_class_add_private (klass, sizeof (GDBusProxyPrivate));
442 }
443
444 static void
445 g_dbus_proxy_init (GDBusProxy *proxy)
446 {
447   proxy->priv = G_TYPE_INSTANCE_GET_PRIVATE (proxy, G_TYPE_DBUS_PROXY, GDBusProxyPrivate);
448 }
449
450 /* ---------------------------------------------------------------------------------------------------- */
451
452 /**
453  * g_dbus_proxy_get_cached_property_names:
454  * @proxy: A #GDBusProxy.
455  * @error: Return location for error or %NULL.
456  *
457  * Gets the names of all cached properties on @proxy.
458  *
459  * Returns: A %NULL-terminated array of strings or %NULL if @error is set. Free with
460  * g_strfreev().
461  *
462  * Since: 2.26
463  */
464 gchar **
465 g_dbus_proxy_get_cached_property_names (GDBusProxy  *proxy,
466                                         GError     **error)
467 {
468   gchar **names;
469   GPtrArray *p;
470   GHashTableIter iter;
471   const gchar *key;
472
473   g_return_val_if_fail (G_IS_DBUS_PROXY (proxy), NULL);
474   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
475
476   names = NULL;
477
478   if (proxy->priv->flags & G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES)
479     {
480       g_set_error (error,
481                    G_IO_ERROR,
482                    G_IO_ERROR_FAILED,
483                    _("Properties are not available (proxy created with G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES)"));
484       goto out;
485     }
486
487   p = g_ptr_array_new ();
488
489   g_hash_table_iter_init (&iter, proxy->priv->properties);
490   while (g_hash_table_iter_next (&iter, (gpointer) &key, NULL))
491     g_ptr_array_add (p, g_strdup (key));
492   g_ptr_array_sort (p, (GCompareFunc) g_strcmp0);
493   g_ptr_array_add (p, NULL);
494
495   names = (gchar **) g_ptr_array_free (p, FALSE);
496
497  out:
498   return names;
499 }
500
501 static const GDBusPropertyInfo *
502 lookup_property_info_or_warn (GDBusProxy  *proxy,
503                               const gchar *property_name)
504 {
505   const GDBusPropertyInfo *info;
506
507   if (proxy->priv->expected_interface == NULL)
508     return NULL;
509
510   info = g_dbus_interface_info_lookup_property (proxy->priv->expected_interface, property_name);
511   if (info == NULL)
512     {
513       g_warning ("Trying to lookup property %s which isn't in expected interface %s",
514                  property_name,
515                  proxy->priv->expected_interface->name);
516     }
517
518   return info;
519 }
520
521 /**
522  * g_dbus_proxy_get_cached_property:
523  * @proxy: A #GDBusProxy.
524  * @property_name: Property name.
525  *
526  * Looks up the value for a property from the cache. This call does no
527  * blocking IO.
528  *
529  * If @proxy has an expected interface (see
530  * #GDBusProxy:g-interface-info), then @property_name (for existence)
531  * is checked against it.
532  *
533  * Returns: A reference to the #GVariant instance that holds the value
534  * for @property_name or %NULL if the value is not in the cache. The
535  * returned reference must be freed with g_variant_unref().
536  *
537  * Since: 2.26
538  */
539 GVariant *
540 g_dbus_proxy_get_cached_property (GDBusProxy   *proxy,
541                                   const gchar  *property_name)
542 {
543   GVariant *value;
544
545   g_return_val_if_fail (G_IS_DBUS_PROXY (proxy), NULL);
546   g_return_val_if_fail (property_name != NULL, NULL);
547
548   value = g_hash_table_lookup (proxy->priv->properties, property_name);
549   if (value == NULL)
550     {
551       const GDBusPropertyInfo *info;
552       info = lookup_property_info_or_warn (proxy, property_name);
553       /* no difference */
554       goto out;
555     }
556
557   g_variant_ref (value);
558
559  out:
560   return value;
561 }
562
563 /**
564  * g_dbus_proxy_set_cached_property:
565  * @proxy: A #GDBusProxy
566  * @property_name: Property name.
567  * @value: Value for the property or %NULL to remove it from the cache.
568  *
569  * If @value is not %NULL, sets the cached value for the property with
570  * name @property_name to the value in @value.
571  *
572  * If @value is %NULL, then the cached value is removed from the
573  * property cache.
574  *
575  * If @proxy has an expected interface (see
576  * #GDBusProxy:g-interface-info), then @property_name (for existence)
577  * and @value (for the type) is checked against it.
578  *
579  * If the @value #GVariant is floating, it is consumed. This allows
580  * convenient 'inline' use of g_variant_new(), e.g.
581  * |[
582  *  g_dbus_proxy_set_cached_property (proxy,
583  *                                    "SomeProperty",
584  *                                    g_variant_new ("(si)",
585  *                                                  "A String",
586  *                                                  42));
587  * ]|
588  *
589  * Normally you will not need to use this method since @proxy is
590  * tracking changes using the
591  * <literal>org.freedesktop.DBus.Properties.PropertiesChanged</literal>
592  * D-Bus signal. However, for performance reasons an object may decide
593  * to not use this signal for some properties and instead use a
594  * proprietary out-of-band mechanism to transmit changes.
595  *
596  * As a concrete example, consider an object with a property
597  * <literal>ChatroomParticipants</literal> which is an array of
598  * strings. Instead of transmitting the same (long) array every time
599  * the property changes, it is more efficient to only transmit the
600  * delta using e.g. signals <literal>ChatroomParticipantJoined(String
601  * name)</literal> and <literal>ChatroomParticipantParted(String
602  * name)</literal>.
603  *
604  * Since: 2.26
605  */
606 void
607 g_dbus_proxy_set_cached_property (GDBusProxy   *proxy,
608                                   const gchar  *property_name,
609                                   GVariant     *value)
610 {
611   const GDBusPropertyInfo *info;
612
613   g_return_if_fail (G_IS_DBUS_PROXY (proxy));
614   g_return_if_fail (property_name != NULL);
615
616   if (value != NULL)
617     {
618       info = lookup_property_info_or_warn (proxy, property_name);
619       if (info != NULL)
620         {
621           if (g_strcmp0 (info->signature, g_variant_get_type_string (value)) != 0)
622             {
623               g_warning (_("Trying to set property %s of type %s but according to the expected "
624                            "interface the type is %s"),
625                          property_name,
626                          g_variant_get_type_string (value),
627                          info->signature);
628               goto out;
629             }
630         }
631       g_hash_table_insert (proxy->priv->properties,
632                            g_strdup (property_name),
633                            g_variant_ref_sink (value));
634     }
635   else
636     {
637       g_hash_table_remove (proxy->priv->properties, property_name);
638     }
639
640  out:
641   ;
642 }
643
644 /* ---------------------------------------------------------------------------------------------------- */
645
646 static void
647 on_signal_received (GDBusConnection *connection,
648                     const gchar     *sender_name,
649                     const gchar     *object_path,
650                     const gchar     *interface_name,
651                     const gchar     *signal_name,
652                     GVariant        *parameters,
653                     gpointer         user_data)
654 {
655   GDBusProxy *proxy = G_DBUS_PROXY (user_data);
656
657   g_signal_emit (proxy,
658                  signals[SIGNAL_SIGNAL],
659                  0,
660                  sender_name,
661                  signal_name,
662                  parameters);
663 }
664
665 /* ---------------------------------------------------------------------------------------------------- */
666
667 static void
668 on_properties_changed (GDBusConnection *connection,
669                        const gchar     *sender_name,
670                        const gchar     *object_path,
671                        const gchar     *interface_name,
672                        const gchar     *signal_name,
673                        GVariant        *parameters,
674                        gpointer         user_data)
675 {
676   GDBusProxy *proxy = G_DBUS_PROXY (user_data);
677   GError *error;
678   const gchar *interface_name_for_signal;
679   GVariantIter *iter;
680   GVariantIter *invalidated_iter;
681   GVariant *item;
682   GVariant *changed_properties;
683   GVariantBuilder *builder;
684   GPtrArray *p;
685   const gchar *str;
686   gchar **invalidated_properties;
687
688   error = NULL;
689   iter = NULL;
690   invalidated_iter = NULL;
691
692 #if 0 // TODO!
693   /* Ignore this signal if properties are not yet available
694    *
695    * (can happen in the window between subscribing to PropertiesChanged() and until
696    *  org.freedesktop.DBus.Properties.GetAll() returns)
697    */
698   if (!proxy->priv->properties_available)
699     goto out;
700 #endif
701
702   if (strcmp (g_variant_get_type_string (parameters), "(sa{sv}as)") != 0)
703     {
704       g_warning ("Value for PropertiesChanged signal with type `%s' does not match `(sa{sv}as)'",
705                  g_variant_get_type_string (parameters));
706       goto out;
707     }
708
709   g_variant_get (parameters,
710                  "(sa{sv}as)",
711                  &interface_name_for_signal,
712                  &iter,
713                  &invalidated_iter);
714
715   if (g_strcmp0 (interface_name_for_signal, proxy->priv->interface_name) != 0)
716     goto out;
717
718   builder = NULL;
719   while ((item = g_variant_iter_next_value (iter)))
720     {
721       const gchar *key;
722       GVariant *value;
723
724       if (builder == NULL)
725         builder = g_variant_builder_new (G_VARIANT_TYPE_ARRAY);
726
727       g_variant_get (item,
728                      "{sv}",
729                      &key,
730                      &value);
731
732       g_hash_table_insert (proxy->priv->properties,
733                            g_strdup (key),
734                            value); /* steals value */
735
736       g_variant_builder_add (builder,
737                              "{sv}",
738                              g_strdup (key),
739                              g_variant_ref (value));
740     }
741   if (builder != NULL)
742     changed_properties = g_variant_builder_end (builder);
743   else
744     changed_properties = NULL;
745
746   p = NULL;
747   while (g_variant_iter_loop (invalidated_iter, "s", &str))
748     {
749       if (p == NULL)
750         p = g_ptr_array_new ();
751       g_ptr_array_add (p, (gpointer) str);
752     }
753   if (p != NULL)
754     {
755       g_ptr_array_add (p, NULL);
756       invalidated_properties = (gchar **) g_ptr_array_free (p, FALSE);
757     }
758   else
759     {
760       invalidated_properties = NULL;
761     }
762
763
764   /* emit signal */
765   g_signal_emit (proxy, signals[PROPERTIES_CHANGED_SIGNAL],
766                  0,
767                  changed_properties,
768                  invalidated_properties);
769
770   if (changed_properties != NULL)
771     g_variant_unref (changed_properties);
772   if (invalidated_properties != NULL)
773     g_free (invalidated_properties);
774
775  out:
776   if (iter != NULL)
777     g_variant_iter_free (iter);
778   if (invalidated_iter != NULL)
779     g_variant_iter_free (invalidated_iter);
780 }
781
782 /* ---------------------------------------------------------------------------------------------------- */
783
784 static void
785 g_dbus_proxy_constructed (GObject *object)
786 {
787   if (G_OBJECT_CLASS (g_dbus_proxy_parent_class)->constructed != NULL)
788     G_OBJECT_CLASS (g_dbus_proxy_parent_class)->constructed (object);
789 }
790
791 /* ---------------------------------------------------------------------------------------------------- */
792
793 static void
794 subscribe_to_signals (GDBusProxy *proxy)
795 {
796   if (!(proxy->priv->flags & G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES))
797     {
798       /* subscribe to PropertiesChanged() */
799       proxy->priv->properties_changed_subscriber_id =
800         g_dbus_connection_signal_subscribe (proxy->priv->connection,
801                                             proxy->priv->unique_bus_name,
802                                             "org.freedesktop.DBus.Properties",
803                                             "PropertiesChanged",
804                                             proxy->priv->object_path,
805                                             proxy->priv->interface_name,
806                                             on_properties_changed,
807                                             proxy,
808                                             NULL);
809     }
810
811   if (!(proxy->priv->flags & G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS))
812     {
813       /* subscribe to all signals for the object */
814       proxy->priv->signals_subscriber_id =
815         g_dbus_connection_signal_subscribe (proxy->priv->connection,
816                                             proxy->priv->unique_bus_name,
817                                             proxy->priv->interface_name,
818                                             NULL,                        /* member */
819                                             proxy->priv->object_path,
820                                             NULL,                        /* arg0 */
821                                             on_signal_received,
822                                             proxy,
823                                             NULL);
824     }
825 }
826
827 /* ---------------------------------------------------------------------------------------------------- */
828
829 static void
830 process_get_all_reply (GDBusProxy *proxy,
831                        GVariant   *result)
832 {
833   GVariantIter iter;
834   GVariant *item;
835
836   if (strcmp (g_variant_get_type_string (result), "(a{sv})") != 0)
837     {
838       g_warning ("Value for GetAll reply with type `%s' does not match `(a{sv})'",
839                  g_variant_get_type_string (result));
840       goto out;
841     }
842
843   proxy->priv->properties = g_hash_table_new_full (g_str_hash,
844                                                    g_str_equal,
845                                                    g_free,
846                                                    (GDestroyNotify) g_variant_unref);
847
848   g_variant_iter_init (&iter, g_variant_get_child_value (result, 0));
849   while ((item = g_variant_iter_next_value (&iter)) != NULL)
850     {
851       const gchar *key;
852       GVariant *value;
853
854       g_variant_get (item,
855                      "{sv}",
856                      &key,
857                      &value);
858       //g_print ("got %s -> %s\n", key, g_variant_markup_print (value, FALSE, 0, 0));
859
860       g_hash_table_insert (proxy->priv->properties,
861                            g_strdup (key),
862                            value); /* steals value */
863     }
864  out:
865   ;
866 }
867
868 static gboolean
869 initable_init (GInitable     *initable,
870                GCancellable  *cancellable,
871                GError       **error)
872 {
873   GDBusProxy *proxy = G_DBUS_PROXY (initable);
874   GVariant *result;
875   gboolean ret;
876
877   ret = FALSE;
878
879   if (!(proxy->priv->flags & G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES))
880     {
881       /* load all properties synchronously */
882       result = g_dbus_connection_call_sync (proxy->priv->connection,
883                                             proxy->priv->unique_bus_name,
884                                             proxy->priv->object_path,
885                                             "org.freedesktop.DBus.Properties",
886                                             "GetAll",
887                                             g_variant_new ("(s)", proxy->priv->interface_name),
888                                             G_DBUS_CALL_FLAGS_NONE,
889                                             -1,           /* timeout */
890                                             cancellable,
891                                             error);
892       if (result == NULL)
893         goto out;
894
895       process_get_all_reply (proxy, result);
896
897       g_variant_unref (result);
898     }
899
900   subscribe_to_signals (proxy);
901
902   ret = TRUE;
903
904  out:
905   return ret;
906 }
907
908 static void
909 initable_iface_init (GInitableIface *initable_iface)
910 {
911   initable_iface->init = initable_init;
912 }
913
914 /* ---------------------------------------------------------------------------------------------------- */
915
916 static void
917 get_all_cb (GDBusConnection *connection,
918             GAsyncResult    *res,
919             gpointer         user_data)
920 {
921   GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
922   GVariant *result;
923   GError *error;
924
925   error = NULL;
926   result = g_dbus_connection_call_finish (connection,
927                                           res,
928                                           &error);
929   if (result == NULL)
930     {
931       g_simple_async_result_set_from_error (simple, error);
932       g_error_free (error);
933     }
934   else
935     {
936       g_simple_async_result_set_op_res_gpointer (simple,
937                                                  result,
938                                                  (GDestroyNotify) g_variant_unref);
939     }
940
941   g_simple_async_result_complete_in_idle (simple);
942   g_object_unref (simple);
943 }
944
945 static void
946 async_initable_init_async (GAsyncInitable      *initable,
947                            gint                 io_priority,
948                            GCancellable        *cancellable,
949                            GAsyncReadyCallback  callback,
950                            gpointer             user_data)
951 {
952   GDBusProxy *proxy = G_DBUS_PROXY (initable);
953   GSimpleAsyncResult *simple;
954
955   simple = g_simple_async_result_new (G_OBJECT (proxy),
956                                       callback,
957                                       user_data,
958                                       NULL);
959
960   if (!(proxy->priv->flags & G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES))
961     {
962       /* load all properties asynchronously */
963       g_dbus_connection_call (proxy->priv->connection,
964                               proxy->priv->unique_bus_name,
965                               proxy->priv->object_path,
966                               "org.freedesktop.DBus.Properties",
967                               "GetAll",
968                               g_variant_new ("(s)", proxy->priv->interface_name),
969                               G_DBUS_CALL_FLAGS_NONE,
970                               -1,           /* timeout */
971                               cancellable,
972                               (GAsyncReadyCallback) get_all_cb,
973                               simple);
974     }
975   else
976     {
977       g_simple_async_result_complete_in_idle (simple);
978       g_object_unref (simple);
979     }
980 }
981
982 static gboolean
983 async_initable_init_finish (GAsyncInitable  *initable,
984                             GAsyncResult    *res,
985                             GError         **error)
986 {
987   GDBusProxy *proxy = G_DBUS_PROXY (initable);
988   GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
989   GVariant *result;
990   gboolean ret;
991
992   ret = FALSE;
993
994   result = g_simple_async_result_get_op_res_gpointer (simple);
995   if (result == NULL)
996     {
997       if (!(proxy->priv->flags & G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES))
998         {
999           g_simple_async_result_propagate_error (simple, error);
1000           goto out;
1001         }
1002     }
1003   else
1004     {
1005       process_get_all_reply (proxy, result);
1006     }
1007
1008   subscribe_to_signals (proxy);
1009
1010   ret = TRUE;
1011
1012  out:
1013   return ret;
1014 }
1015
1016 static void
1017 async_initable_iface_init (GAsyncInitableIface *async_initable_iface)
1018 {
1019   async_initable_iface->init_async = async_initable_init_async;
1020   async_initable_iface->init_finish = async_initable_init_finish;
1021 }
1022
1023 /* ---------------------------------------------------------------------------------------------------- */
1024
1025 /**
1026  * g_dbus_proxy_new:
1027  * @connection: A #GDBusConnection.
1028  * @object_type: Either #G_TYPE_DBUS_PROXY or the #GType for the #GDBusProxy<!-- -->-derived type of proxy to create.
1029  * @flags: Flags used when constructing the proxy.
1030  * @info: A #GDBusInterfaceInfo specifying the minimal interface that @proxy conforms to or %NULL.
1031  * @unique_bus_name: A unique bus name or %NULL if @connection is not a message bus connection.
1032  * @object_path: An object path.
1033  * @interface_name: A D-Bus interface name.
1034  * @cancellable: A #GCancellable or %NULL.
1035  * @callback: Callback function to invoke when the proxy is ready.
1036  * @user_data: User data to pass to @callback.
1037  *
1038  * Creates a proxy for accessing @interface_name on the remote object at @object_path
1039  * owned by @unique_bus_name at @connection and asynchronously loads D-Bus properties unless the
1040  * #G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES flag is used. Connect to the
1041  * #GDBusProxy::g-properties-changed signal to get notified about property changes.
1042  *
1043  * If the #G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS flag is not set, also sets up
1044  * match rules for signals. Connect to the #GDBusProxy::g-signal signal
1045  * to handle signals from the remote object.
1046  *
1047  * This is a failable asynchronous constructor - when the proxy is
1048  * ready, @callback will be invoked and you can use
1049  * g_dbus_proxy_new_finish() to get the result.
1050  *
1051  * See g_dbus_proxy_new_sync() and for a synchronous version of this constructor.
1052  *
1053  * Since: 2.26
1054  */
1055 void
1056 g_dbus_proxy_new (GDBusConnection     *connection,
1057                   GType                object_type,
1058                   GDBusProxyFlags      flags,
1059                   GDBusInterfaceInfo  *info,
1060                   const gchar         *unique_bus_name,
1061                   const gchar         *object_path,
1062                   const gchar         *interface_name,
1063                   GCancellable        *cancellable,
1064                   GAsyncReadyCallback  callback,
1065                   gpointer             user_data)
1066 {
1067   g_return_if_fail (G_IS_DBUS_CONNECTION (connection));
1068   g_return_if_fail (g_type_is_a (object_type, G_TYPE_DBUS_PROXY));
1069   g_return_if_fail ((unique_bus_name == NULL && g_dbus_connection_get_unique_name (connection) == NULL) ||
1070                     g_dbus_is_unique_name (unique_bus_name));
1071   g_return_if_fail (g_variant_is_object_path (object_path));
1072   g_return_if_fail (g_dbus_is_interface_name (interface_name));
1073
1074   g_async_initable_new_async (object_type,
1075                               G_PRIORITY_DEFAULT,
1076                               cancellable,
1077                               callback,
1078                               user_data,
1079                               "g-flags", flags,
1080                               "g-interface-info", info,
1081                               "g-unique-bus-name", unique_bus_name,
1082                               "g-connection", connection,
1083                               "g-object-path", object_path,
1084                               "g-interface-name", interface_name,
1085                               NULL);
1086 }
1087
1088 /**
1089  * g_dbus_proxy_new_finish:
1090  * @res: A #GAsyncResult obtained from the #GAsyncReadyCallback function passed to g_dbus_proxy_new().
1091  * @error: Return location for error or %NULL.
1092  *
1093  * Finishes creating a #GDBusProxy.
1094  *
1095  * Returns: A #GDBusProxy or %NULL if @error is set. Free with g_object_unref().
1096  *
1097  * Since: 2.26
1098  */
1099 GDBusProxy *
1100 g_dbus_proxy_new_finish (GAsyncResult  *res,
1101                          GError       **error)
1102 {
1103   GObject *object;
1104   GObject *source_object;
1105
1106   source_object = g_async_result_get_source_object (res);
1107   g_assert (source_object != NULL);
1108
1109   object = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object),
1110                                         res,
1111                                         error);
1112   g_object_unref (source_object);
1113
1114   if (object != NULL)
1115     return G_DBUS_PROXY (object);
1116   else
1117     return NULL;
1118 }
1119
1120
1121 /* ---------------------------------------------------------------------------------------------------- */
1122
1123 /**
1124  * g_dbus_proxy_new_sync:
1125  * @connection: A #GDBusConnection.
1126  * @object_type: Either #G_TYPE_DBUS_PROXY or the #GType for the #GDBusProxy<!-- -->-derived type of proxy to create.
1127  * @flags: Flags used when constructing the proxy.
1128  * @info: A #GDBusInterfaceInfo specifying the minimal interface that @proxy conforms to or %NULL.
1129  * @unique_bus_name: A unique bus name or %NULL if @connection is not a message bus connection.
1130  * @object_path: An object path.
1131  * @interface_name: A D-Bus interface name.
1132  * @cancellable: A #GCancellable or %NULL.
1133  * @error: Return location for error or %NULL.
1134  *
1135  * Creates a proxy for accessing @interface_name on the remote object at @object_path
1136  * owned by @unique_bus_name at @connection and synchronously loads D-Bus properties unless the
1137  * #G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES flag is used.
1138  *
1139  * If the #G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS flag is not set, also sets up
1140  * match rules for signals. Connect to the #GDBusProxy::g-signal signal
1141  * to handle signals from the remote object.
1142  *
1143  * This is a synchronous failable constructor. See g_dbus_proxy_new()
1144  * and g_dbus_proxy_new_finish() for the asynchronous version.
1145  *
1146  * Returns: A #GDBusProxy or %NULL if error is set. Free with g_object_unref().
1147  *
1148  * Since: 2.26
1149  */
1150 GDBusProxy *
1151 g_dbus_proxy_new_sync (GDBusConnection     *connection,
1152                        GType                object_type,
1153                        GDBusProxyFlags      flags,
1154                        GDBusInterfaceInfo  *info,
1155                        const gchar         *unique_bus_name,
1156                        const gchar         *object_path,
1157                        const gchar         *interface_name,
1158                        GCancellable        *cancellable,
1159                        GError             **error)
1160 {
1161   GInitable *initable;
1162
1163   g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL);
1164   g_return_val_if_fail (g_type_is_a (object_type, G_TYPE_DBUS_PROXY), NULL);
1165   g_return_val_if_fail ((unique_bus_name == NULL && g_dbus_connection_get_unique_name (connection) == NULL) ||
1166                         g_dbus_is_unique_name (unique_bus_name), NULL);
1167   g_return_val_if_fail (g_variant_is_object_path (object_path), NULL);
1168   g_return_val_if_fail (g_dbus_is_interface_name (interface_name), NULL);
1169
1170   initable = g_initable_new (object_type,
1171                              cancellable,
1172                              error,
1173                              "g-flags", flags,
1174                              "g-interface-info", info,
1175                              "g-unique-bus-name", unique_bus_name,
1176                              "g-connection", connection,
1177                              "g-object-path", object_path,
1178                              "g-interface-name", interface_name,
1179                              NULL);
1180   if (initable != NULL)
1181     return G_DBUS_PROXY (initable);
1182   else
1183     return NULL;
1184 }
1185
1186 /* ---------------------------------------------------------------------------------------------------- */
1187
1188 /**
1189  * g_dbus_proxy_get_connection:
1190  * @proxy: A #GDBusProxy.
1191  *
1192  * Gets the connection @proxy is for.
1193  *
1194  * Returns: A #GDBusConnection owned by @proxy. Do not free.
1195  *
1196  * Since: 2.26
1197  */
1198 GDBusConnection *
1199 g_dbus_proxy_get_connection (GDBusProxy *proxy)
1200 {
1201   g_return_val_if_fail (G_IS_DBUS_PROXY (proxy), NULL);
1202   return proxy->priv->connection;
1203 }
1204
1205 /**
1206  * g_dbus_proxy_get_flags:
1207  * @proxy: A #GDBusProxy.
1208  *
1209  * Gets the flags that @proxy was constructed with.
1210  *
1211  * Returns: Flags from the #GDBusProxyFlags enumeration.
1212  *
1213  * Since: 2.26
1214  */
1215 GDBusProxyFlags
1216 g_dbus_proxy_get_flags (GDBusProxy *proxy)
1217 {
1218   g_return_val_if_fail (G_IS_DBUS_PROXY (proxy), 0);
1219   return proxy->priv->flags;
1220 }
1221
1222 /**
1223  * g_dbus_proxy_get_unique_bus_name:
1224  * @proxy: A #GDBusProxy.
1225  *
1226  * Gets the unique bus name @proxy is for.
1227  *
1228  * Returns: A string owned by @proxy. Do not free.
1229  *
1230  * Since: 2.26
1231  */
1232 const gchar *
1233 g_dbus_proxy_get_unique_bus_name (GDBusProxy *proxy)
1234 {
1235   g_return_val_if_fail (G_IS_DBUS_PROXY (proxy), NULL);
1236   return proxy->priv->unique_bus_name;
1237 }
1238
1239 /**
1240  * g_dbus_proxy_get_object_path:
1241  * @proxy: A #GDBusProxy.
1242  *
1243  * Gets the object path @proxy is for.
1244  *
1245  * Returns: A string owned by @proxy. Do not free.
1246  *
1247  * Since: 2.26
1248  */
1249 const gchar *
1250 g_dbus_proxy_get_object_path (GDBusProxy *proxy)
1251 {
1252   g_return_val_if_fail (G_IS_DBUS_PROXY (proxy), NULL);
1253   return proxy->priv->object_path;
1254 }
1255
1256 /**
1257  * g_dbus_proxy_get_interface_name:
1258  * @proxy: A #GDBusProxy.
1259  *
1260  * Gets the D-Bus interface name @proxy is for.
1261  *
1262  * Returns: A string owned by @proxy. Do not free.
1263  *
1264  * Since: 2.26
1265  */
1266 const gchar *
1267 g_dbus_proxy_get_interface_name (GDBusProxy *proxy)
1268 {
1269   g_return_val_if_fail (G_IS_DBUS_PROXY (proxy), NULL);
1270   return proxy->priv->interface_name;
1271 }
1272
1273 /**
1274  * g_dbus_proxy_get_default_timeout:
1275  * @proxy: A #GDBusProxy.
1276  *
1277  * Gets the timeout to use if -1 (specifying default timeout) is
1278  * passed as @timeout_msec in the g_dbus_proxy_call() and
1279  * g_dbus_proxy_call_sync() functions.
1280  *
1281  * See the #GDBusProxy:g-default-timeout property for more details.
1282  *
1283  * Returns: Timeout to use for @proxy.
1284  *
1285  * Since: 2.26
1286  */
1287 gint
1288 g_dbus_proxy_get_default_timeout (GDBusProxy *proxy)
1289 {
1290   g_return_val_if_fail (G_IS_DBUS_PROXY (proxy), -1);
1291   return proxy->priv->timeout_msec;
1292 }
1293
1294 /**
1295  * g_dbus_proxy_set_default_timeout:
1296  * @proxy: A #GDBusProxy.
1297  * @timeout_msec: Timeout in milliseconds.
1298  *
1299  * Sets the timeout to use if -1 (specifying default timeout) is
1300  * passed as @timeout_msec in the g_dbus_proxy_call() and
1301  * g_dbus_proxy_call_sync() functions.
1302  *
1303  * See the #GDBusProxy:g-default-timeout property for more details.
1304  *
1305  * Since: 2.26
1306  */
1307 void
1308 g_dbus_proxy_set_default_timeout (GDBusProxy *proxy,
1309                                   gint        timeout_msec)
1310 {
1311   g_return_if_fail (G_IS_DBUS_PROXY (proxy));
1312   g_return_if_fail (timeout_msec == -1 || timeout_msec >= 0);
1313
1314   /* TODO: locking? */
1315   if (proxy->priv->timeout_msec != timeout_msec)
1316     {
1317       proxy->priv->timeout_msec = timeout_msec;
1318       g_object_notify (G_OBJECT (proxy), "g-default-timeout");
1319     }
1320 }
1321
1322 /**
1323  * g_dbus_proxy_get_interface_info:
1324  * @proxy: A #GDBusProxy
1325  *
1326  * Returns the #GDBusInterfaceInfo, if any, specifying the minimal
1327  * interface that @proxy conforms to.
1328  *
1329  * See the #GDBusProxy:g-interface-info property for more details.
1330  *
1331  * Returns: A #GDBusInterfaceInfo or %NULL. Do not unref the returned
1332  * object, it is owned by @proxy.
1333  *
1334  * Since: 2.26
1335  */
1336 GDBusInterfaceInfo *
1337 g_dbus_proxy_get_interface_info (GDBusProxy *proxy)
1338 {
1339   g_return_val_if_fail (G_IS_DBUS_PROXY (proxy), NULL);
1340   return proxy->priv->expected_interface;
1341 }
1342
1343 /**
1344  * g_dbus_proxy_set_interface_info:
1345  * @proxy: A #GDBusProxy
1346  * @info: Minimum interface this proxy conforms to or %NULL to unset.
1347  *
1348  * Ensure that interactions with @proxy conform to the given
1349  * interface.  For example, when completing a method call, if the type
1350  * signature of the message isn't what's expected, the given #GError
1351  * is set.  Signals that have a type signature mismatch are simply
1352  * dropped.
1353  *
1354  * See the #GDBusProxy:g-interface-info property for more details.
1355  *
1356  * Since: 2.26
1357  */
1358 void
1359 g_dbus_proxy_set_interface_info (GDBusProxy         *proxy,
1360                                  GDBusInterfaceInfo *info)
1361 {
1362   g_return_if_fail (G_IS_DBUS_PROXY (proxy));
1363   if (proxy->priv->expected_interface != NULL)
1364     g_dbus_interface_info_unref (proxy->priv->expected_interface);
1365   proxy->priv->expected_interface = info != NULL ? g_dbus_interface_info_ref (info) : NULL;
1366 }
1367
1368 /* ---------------------------------------------------------------------------------------------------- */
1369
1370 static gboolean
1371 maybe_split_method_name (const gchar  *method_name,
1372                          gchar       **out_interface_name,
1373                          const gchar **out_method_name)
1374 {
1375   gboolean was_split;
1376
1377   was_split = FALSE;
1378   g_assert (out_interface_name != NULL);
1379   g_assert (out_method_name != NULL);
1380   *out_interface_name = NULL;
1381   *out_method_name = NULL;
1382
1383   if (strchr (method_name, '.') != NULL)
1384     {
1385       gchar *p;
1386       gchar *last_dot;
1387
1388       p = g_strdup (method_name);
1389       last_dot = strrchr (p, '.');
1390       *last_dot = '\0';
1391
1392       *out_interface_name = p;
1393       *out_method_name = last_dot + 1;
1394
1395       was_split = TRUE;
1396     }
1397
1398   return was_split;
1399 }
1400
1401
1402 static void
1403 reply_cb (GDBusConnection *connection,
1404           GAsyncResult    *res,
1405           gpointer         user_data)
1406 {
1407   GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
1408   GVariant *value;
1409   GError *error;
1410
1411   error = NULL;
1412   value = g_dbus_connection_call_finish (connection,
1413                                          res,
1414                                          &error);
1415   if (error != NULL)
1416     {
1417       g_simple_async_result_set_from_error (simple,
1418                                             error);
1419       g_error_free (error);
1420     }
1421   else
1422     {
1423       g_simple_async_result_set_op_res_gpointer (simple,
1424                                                  value,
1425                                                  (GDestroyNotify) g_variant_unref);
1426     }
1427
1428   /* no need to complete in idle since the method GDBusConnection already does */
1429   g_simple_async_result_complete (simple);
1430 }
1431
1432 static const GDBusMethodInfo *
1433 lookup_method_info_or_warn (GDBusProxy  *proxy,
1434                             const gchar *method_name)
1435 {
1436   const GDBusMethodInfo *info;
1437
1438   if (proxy->priv->expected_interface == NULL)
1439     return NULL;
1440
1441   info = g_dbus_interface_info_lookup_method (proxy->priv->expected_interface, method_name);
1442   if (info == NULL)
1443     {
1444       g_warning ("Trying to invoke method %s which isn't in expected interface %s",
1445                  method_name, proxy->priv->expected_interface->name);
1446     }
1447
1448   return info;
1449 }
1450
1451 static gboolean
1452 validate_method_return (const char             *method_name,
1453                         GVariant               *value,
1454                         const GDBusMethodInfo  *expected_method_info,
1455                         GError                **error)
1456 {
1457   const gchar *type_string;
1458   gchar *signature;
1459   gboolean ret;
1460
1461   ret = TRUE;
1462   signature = NULL;
1463
1464   if (value == NULL || expected_method_info == NULL)
1465     goto out;
1466
1467   /* Shouldn't happen... */
1468   if (g_variant_classify (value) != G_VARIANT_CLASS_TUPLE)
1469     goto out;
1470
1471   type_string = g_variant_get_type_string (value);
1472   signature = _g_dbus_compute_complete_signature (expected_method_info->out_args, TRUE);
1473   if (g_strcmp0 (type_string, signature) != 0)
1474     {
1475       g_set_error (error,
1476                    G_IO_ERROR,
1477                    G_IO_ERROR_INVALID_ARGUMENT,
1478                    _("Method `%s' returned signature `%s', but expected `%s'"),
1479                    method_name,
1480                    type_string,
1481                    signature);
1482       ret = FALSE;
1483     }
1484
1485  out:
1486   g_free (signature);
1487   return ret;
1488 }
1489
1490 /**
1491  * g_dbus_proxy_call:
1492  * @proxy: A #GDBusProxy.
1493  * @method_name: Name of method to invoke.
1494  * @parameters: A #GVariant tuple with parameters for the signal or %NULL if not passing parameters.
1495  * @flags: Flags from the #GDBusCallFlags enumeration.
1496  * @timeout_msec: The timeout in milliseconds or -1 to use the proxy default timeout.
1497  * @cancellable: A #GCancellable or %NULL.
1498  * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL if you don't
1499  * care about the result of the method invocation.
1500  * @user_data: The data to pass to @callback.
1501  *
1502  * Asynchronously invokes the @method_name method on @proxy.
1503  *
1504  * If @method_name contains any dots, then @name is split into interface and
1505  * method name parts. This allows using @proxy for invoking methods on
1506  * other interfaces.
1507  *
1508  * If the #GDBusConnection associated with @proxy is closed then
1509  * the operation will fail with %G_IO_ERROR_CLOSED. If
1510  * @cancellable is canceled, the operation will fail with
1511  * %G_IO_ERROR_CANCELLED. If @parameters contains a value not
1512  * compatible with the D-Bus protocol, the operation fails with
1513  * %G_IO_ERROR_INVALID_ARGUMENT.
1514  *
1515  * If the @parameters #GVariant is floating, it is consumed. This allows
1516  * convenient 'inline' use of g_variant_new(), e.g.:
1517  * |[
1518  *  g_dbus_proxy_call (proxy,
1519  *                     "TwoStrings",
1520  *                     g_variant_new ("(ss)",
1521  *                                    "Thing One",
1522  *                                    "Thing Two"),
1523  *                     G_DBUS_CALL_FLAGS_NONE,
1524  *                     -1,
1525  *                     NULL,
1526  *                     (GAsyncReadyCallback) two_strings_done,
1527  *                     &amp;data);
1528  * ]|
1529  *
1530  * This is an asynchronous method. When the operation is finished,
1531  * @callback will be invoked in the
1532  * <link linkend="g-main-context-push-thread-default">thread-default
1533  * main loop</link> of the thread you are calling this method from.
1534  * You can then call g_dbus_proxy_call_finish() to get the result of
1535  * the operation. See g_dbus_proxy_call_sync() for the synchronous
1536  * version of this method.
1537  *
1538  * Since: 2.26
1539  */
1540 void
1541 g_dbus_proxy_call (GDBusProxy          *proxy,
1542                    const gchar         *method_name,
1543                    GVariant            *parameters,
1544                    GDBusCallFlags       flags,
1545                    gint                 timeout_msec,
1546                    GCancellable        *cancellable,
1547                    GAsyncReadyCallback  callback,
1548                    gpointer             user_data)
1549 {
1550   GSimpleAsyncResult *simple;
1551   gboolean was_split;
1552   gchar *split_interface_name;
1553   const gchar *split_method_name;
1554   const GDBusMethodInfo *expected_method_info;
1555   const gchar *target_method_name;
1556   const gchar *target_interface_name;
1557
1558   g_return_if_fail (G_IS_DBUS_PROXY (proxy));
1559   g_return_if_fail (g_dbus_is_member_name (method_name) || g_dbus_is_interface_name (method_name));
1560   g_return_if_fail (parameters == NULL || g_variant_is_of_type (parameters, G_VARIANT_TYPE_TUPLE));
1561   g_return_if_fail (timeout_msec == -1 || timeout_msec >= 0);
1562
1563   simple = g_simple_async_result_new (G_OBJECT (proxy),
1564                                       callback,
1565                                       user_data,
1566                                       g_dbus_proxy_call);
1567
1568   was_split = maybe_split_method_name (method_name, &split_interface_name, &split_method_name);
1569   target_method_name = was_split ? split_method_name : method_name;
1570   target_interface_name = was_split ? split_interface_name : proxy->priv->interface_name;
1571
1572   g_object_set_data_full (G_OBJECT (simple), "-gdbus-proxy-method-name", g_strdup (target_method_name), g_free);
1573
1574   /* Just warn here */
1575   expected_method_info = lookup_method_info_or_warn (proxy, target_method_name);
1576
1577   g_dbus_connection_call (proxy->priv->connection,
1578                           proxy->priv->unique_bus_name,
1579                           proxy->priv->object_path,
1580                           target_interface_name,
1581                           target_method_name,
1582                           parameters,
1583                           flags,
1584                           timeout_msec == -1 ? proxy->priv->timeout_msec : timeout_msec,
1585                           cancellable,
1586                           (GAsyncReadyCallback) reply_cb,
1587                           simple);
1588
1589   g_free (split_interface_name);
1590 }
1591
1592 /**
1593  * g_dbus_proxy_call_finish:
1594  * @proxy: A #GDBusProxy.
1595  * @res: A #GAsyncResult obtained from the #GAsyncReadyCallback passed to g_dbus_proxy_call().
1596  * @error: Return location for error or %NULL.
1597  *
1598  * Finishes an operation started with g_dbus_proxy_call().
1599  *
1600  * Returns: %NULL if @error is set. Otherwise a #GVariant tuple with
1601  * return values. Free with g_variant_unref().
1602  *
1603  * Since: 2.26
1604  */
1605 GVariant *
1606 g_dbus_proxy_call_finish (GDBusProxy    *proxy,
1607                           GAsyncResult  *res,
1608                           GError       **error)
1609 {
1610   GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
1611   GVariant *value;
1612   const char *method_name;
1613   const GDBusMethodInfo *expected_method_info;
1614
1615   g_return_val_if_fail (G_IS_DBUS_PROXY (proxy), NULL);
1616   g_return_val_if_fail (G_IS_ASYNC_RESULT (res), NULL);
1617   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
1618
1619   g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == g_dbus_proxy_call);
1620
1621   value = NULL;
1622
1623   if (g_simple_async_result_propagate_error (simple, error))
1624     goto out;
1625
1626   value = g_simple_async_result_get_op_res_gpointer (simple);
1627   method_name = g_object_get_data (G_OBJECT (simple), "-gdbus-proxy-method-name");
1628
1629   /* We may not have a method name for internally-generated proxy calls like GetAll */
1630   if (value && method_name && proxy->priv->expected_interface)
1631     {
1632       expected_method_info = g_dbus_interface_info_lookup_method (proxy->priv->expected_interface, method_name);
1633       if (!validate_method_return (method_name, value, expected_method_info, error))
1634         {
1635           g_variant_unref (value);
1636           value = NULL;
1637         }
1638     }
1639
1640  out:
1641   return value;
1642 }
1643
1644 /**
1645  * g_dbus_proxy_call_sync:
1646  * @proxy: A #GDBusProxy.
1647  * @method_name: Name of method to invoke.
1648  * @parameters: A #GVariant tuple with parameters for the signal or %NULL if not passing parameters.
1649  * @flags: Flags from the #GDBusCallFlags enumeration.
1650  * @timeout_msec: The timeout in milliseconds or -1 to use the proxy default timeout.
1651  * @cancellable: A #GCancellable or %NULL.
1652  * @error: Return location for error or %NULL.
1653  *
1654  * Synchronously invokes the @method_name method on @proxy.
1655  *
1656  * If @method_name contains any dots, then @name is split into interface and
1657  * method name parts. This allows using @proxy for invoking methods on
1658  * other interfaces.
1659  *
1660  * If the #GDBusConnection associated with @proxy is disconnected then
1661  * the operation will fail with %G_IO_ERROR_CLOSED. If
1662  * @cancellable is canceled, the operation will fail with
1663  * %G_IO_ERROR_CANCELLED. If @parameters contains a value not
1664  * compatible with the D-Bus protocol, the operation fails with
1665  * %G_IO_ERROR_INVALID_ARGUMENT.
1666  *
1667  * If the @parameters #GVariant is floating, it is consumed. This allows
1668  * convenient 'inline' use of g_variant_new(), e.g.:
1669  * |[
1670  *  g_dbus_proxy_call_sync (proxy,
1671  *                          "TwoStrings",
1672  *                          g_variant_new ("(ss)",
1673  *                                         "Thing One",
1674  *                                         "Thing Two"),
1675  *                          G_DBUS_CALL_FLAGS_NONE,
1676  *                          -1,
1677  *                          NULL,
1678  *                          &amp;error);
1679  * ]|
1680  *
1681  * The calling thread is blocked until a reply is received. See
1682  * g_dbus_proxy_call() for the asynchronous version of this
1683  * method.
1684  *
1685  * Returns: %NULL if @error is set. Otherwise a #GVariant tuple with
1686  * return values. Free with g_variant_unref().
1687  *
1688  * Since: 2.26
1689  */
1690 GVariant *
1691 g_dbus_proxy_call_sync (GDBusProxy      *proxy,
1692                         const gchar     *method_name,
1693                         GVariant        *parameters,
1694                         GDBusCallFlags   flags,
1695                         gint             timeout_msec,
1696                         GCancellable    *cancellable,
1697                         GError         **error)
1698 {
1699   GVariant *ret;
1700   gboolean was_split;
1701   gchar *split_interface_name;
1702   const gchar *split_method_name;
1703   const GDBusMethodInfo *expected_method_info;
1704   const gchar *target_method_name;
1705   const gchar *target_interface_name;
1706
1707   g_return_val_if_fail (G_IS_DBUS_PROXY (proxy), NULL);
1708   g_return_val_if_fail (g_dbus_is_member_name (method_name) || g_dbus_is_interface_name (method_name), NULL);
1709   g_return_val_if_fail (parameters == NULL || g_variant_is_of_type (parameters, G_VARIANT_TYPE_TUPLE), NULL);
1710   g_return_val_if_fail (timeout_msec == -1 || timeout_msec >= 0, NULL);
1711   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
1712
1713   was_split = maybe_split_method_name (method_name, &split_interface_name, &split_method_name);
1714   target_method_name = was_split ? split_method_name : method_name;
1715   target_interface_name = was_split ? split_interface_name : proxy->priv->interface_name;
1716
1717   if (proxy->priv->expected_interface)
1718     {
1719       expected_method_info = g_dbus_interface_info_lookup_method (proxy->priv->expected_interface, target_method_name);
1720       if (expected_method_info == NULL)
1721         {
1722           g_warning ("Trying to invoke method `%s' which isn't in expected interface `%s'",
1723                      target_method_name,
1724                      target_interface_name);
1725         }
1726     }
1727   else
1728     {
1729       expected_method_info = NULL;
1730     }
1731
1732   ret = g_dbus_connection_call_sync (proxy->priv->connection,
1733                                      proxy->priv->unique_bus_name,
1734                                      proxy->priv->object_path,
1735                                      target_interface_name,
1736                                      target_method_name,
1737                                      parameters,
1738                                      flags,
1739                                      timeout_msec == -1 ? proxy->priv->timeout_msec : timeout_msec,
1740                                      cancellable,
1741                                      error);
1742   if (!validate_method_return (target_method_name, ret, expected_method_info, error))
1743     {
1744       g_variant_unref (ret);
1745       ret = NULL;
1746     }
1747
1748   g_free (split_interface_name);
1749
1750   return ret;
1751 }
1752
1753 /* ---------------------------------------------------------------------------------------------------- */
1754
1755 #define __G_DBUS_PROXY_C__
1756 #include "gioaliasdef.c"