gdbusproxy.c 102 KB
Newer Older
1 2
/* GDBus - GLib D-Bus Library
 *
3
 * Copyright (C) 2008-2010 Red Hat, Inc.
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General
 * Public License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 * Boston, MA 02111-1307, USA.
 *
 * Author: David Zeuthen <davidz@redhat.com>
 */

#include "config.h"

#include <stdlib.h>
David Zeuthen's avatar
David Zeuthen committed
26 27
#include <string.h>

28 29 30 31 32 33 34 35 36 37 38
#include "gdbusutils.h"
#include "gdbusproxy.h"
#include "gioenumtypes.h"
#include "gdbusconnection.h"
#include "gdbuserror.h"
#include "gdbusprivate.h"
#include "ginitable.h"
#include "gasyncinitable.h"
#include "gioerror.h"
#include "gasyncresult.h"
#include "gsimpleasyncresult.h"
39
#include "gcancellable.h"
40
#include "gdbusinterface.h"
41

42 43 44 45
#ifdef G_OS_UNIX
#include "gunixfdlist.h"
#endif

David Zeuthen's avatar
David Zeuthen committed
46 47
#include "glibintl.h"

48 49
/**
 * SECTION:gdbusproxy
50
 * @short_description: Client-side D-Bus interface proxy
David Zeuthen's avatar
David Zeuthen committed
51
 * @include: gio/gio.h
52 53
 *
 * #GDBusProxy is a base class used for proxies to access a D-Bus
54 55
 * interface on a remote object. A #GDBusProxy can be constructed for
 * both well-known and unique names.
56
 *
57 58
 * By default, #GDBusProxy will cache all properties (and listen to
 * changes) of the remote object, and proxy all signals that gets
59
 * emitted. This behaviour can be changed by passing suitable
60 61 62 63 64 65 66 67
 * #GDBusProxyFlags when the proxy is created. If the proxy is for a
 * well-known name, the property cache is flushed when the name owner
 * vanishes and reloaded when a name owner appears.
 *
 * If a #GDBusProxy is used for a well-known name, the owner of the
 * name is tracked and can be read from
 * #GDBusProxy:g-name-owner. Connect to the #GObject::notify signal to
 * get notified of changes. Additionally, only signals and property
68 69 70 71 72 73 74
 * changes emitted from the current name owner are considered and
 * calls are always sent to the current name owner. This avoids a
 * number of race conditions when the name is lost by one owner and
 * claimed by another. However, if no name owner currently exists,
 * then calls will be sent to the well-known name which may result in
 * the message bus launching an owner (unless
 * %G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START is set).
75
 *
76 77 78 79 80 81 82 83
 * The generic #GDBusProxy::g-properties-changed and
 * #GDBusProxy::g-signal signals are not very convenient to work
 * with. Therefore, the recommended way of working with proxies is to
 * subclass #GDBusProxy, and have more natural properties and signals
 * in your derived class. See <xref linkend="gdbus-example-gdbus-codegen"/>
 * for how this can easily be done using the
 * <command><link linkend="gdbus-codegen">gdbus-codegen</link></command>
 * tool.
84
 *
85
 * A #GDBusProxy instance can be used from multiple threads but note
86
 * that all signals (e.g. #GDBusProxy::g-signal, #GDBusProxy::g-properties-changed
87 88 89 90
 * and #GObject::notify) are emitted in the
 * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
 * of the thread where the instance was constructed.
 *
91
 * <example id="gdbus-wellknown-proxy"><title>GDBusProxy for a well-known-name</title><programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" parse="text" href="../../../../gio/tests/gdbus-example-watch-proxy.c"><xi:fallback>FIXME: MISSING XINCLUDE CONTENT</xi:fallback></xi:include></programlisting></example>
92 93
 */

94 95 96
/* lock protecting the properties GHashTable */
G_LOCK_DEFINE_STATIC (properties_lock);

97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
/* ---------------------------------------------------------------------------------------------------- */

G_LOCK_DEFINE_STATIC (signal_subscription_lock);

typedef struct
{
  volatile gint ref_count;
  GDBusProxy *proxy;
} SignalSubscriptionData;

static SignalSubscriptionData *
signal_subscription_ref (SignalSubscriptionData *data)
{
  g_atomic_int_inc (&data->ref_count);
  return data;
}

static void
signal_subscription_unref (SignalSubscriptionData *data)
{
  if (g_atomic_int_dec_and_test (&data->ref_count))
    {
      g_slice_free (SignalSubscriptionData, data);
    }
}

/* ---------------------------------------------------------------------------------------------------- */

125 126
struct _GDBusProxyPrivate
{
127
  GBusType bus_type;
128
  GDBusProxyFlags flags;
129
  GDBusConnection *connection;
130 131 132

  gchar *name;
  gchar *name_owner;
133 134 135 136
  gchar *object_path;
  gchar *interface_name;
  gint timeout_msec;

137 138 139 140
  guint name_owner_changed_subscription_id;

  GCancellable *get_all_cancellable;

141 142 143 144 145
  /* gchar* -> GVariant* */
  GHashTable *properties;

  GDBusInterfaceInfo *expected_interface;

146 147
  guint properties_changed_subscription_id;
  guint signals_subscription_id;
148 149

  gboolean initialized;
150 151

  GDBusObject *object;
152 153

  SignalSubscriptionData *signal_subscription_data;
154 155 156 157 158 159
};

enum
{
  PROP_0,
  PROP_G_CONNECTION,
160 161 162
  PROP_G_BUS_TYPE,
  PROP_G_NAME,
  PROP_G_NAME_OWNER,
163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
  PROP_G_FLAGS,
  PROP_G_OBJECT_PATH,
  PROP_G_INTERFACE_NAME,
  PROP_G_DEFAULT_TIMEOUT,
  PROP_G_INTERFACE_INFO
};

enum
{
  PROPERTIES_CHANGED_SIGNAL,
  SIGNAL_SIGNAL,
  LAST_SIGNAL,
};

guint signals[LAST_SIGNAL] = {0};

179
static void dbus_interface_iface_init (GDBusInterfaceIface *dbus_interface_iface);
180 181 182 183
static void initable_iface_init       (GInitableIface *initable_iface);
static void async_initable_iface_init (GAsyncInitableIface *async_initable_iface);

G_DEFINE_TYPE_WITH_CODE (GDBusProxy, g_dbus_proxy, G_TYPE_OBJECT,
184
                         G_IMPLEMENT_INTERFACE (G_TYPE_DBUS_INTERFACE, dbus_interface_iface_init)
185 186 187 188
                         G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init)
                         G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, async_initable_iface_init)
                         );

189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204
static void
g_dbus_proxy_dispose (GObject *object)
{
  GDBusProxy *proxy = G_DBUS_PROXY (object);
  G_LOCK (signal_subscription_lock);
  if (proxy->priv->signal_subscription_data != NULL)
    {
      proxy->priv->signal_subscription_data->proxy = NULL;
      signal_subscription_unref (proxy->priv->signal_subscription_data);
      proxy->priv->signal_subscription_data = NULL;
    }
  G_UNLOCK (signal_subscription_lock);

  G_OBJECT_CLASS (g_dbus_proxy_parent_class)->dispose (object);
}

205 206 207 208 209
static void
g_dbus_proxy_finalize (GObject *object)
{
  GDBusProxy *proxy = G_DBUS_PROXY (object);

210 211 212 213 214 215
  g_warn_if_fail (proxy->priv->get_all_cancellable == NULL);

  if (proxy->priv->name_owner_changed_subscription_id > 0)
    g_dbus_connection_signal_unsubscribe (proxy->priv->connection,
                                          proxy->priv->name_owner_changed_subscription_id);

216
  if (proxy->priv->properties_changed_subscription_id > 0)
Matthias Clasen's avatar
Matthias Clasen committed
217
    g_dbus_connection_signal_unsubscribe (proxy->priv->connection,
218
                                          proxy->priv->properties_changed_subscription_id);
219

220
  if (proxy->priv->signals_subscription_id > 0)
Matthias Clasen's avatar
Matthias Clasen committed
221
    g_dbus_connection_signal_unsubscribe (proxy->priv->connection,
222
                                          proxy->priv->signals_subscription_id);
223

224 225
  if (proxy->priv->connection != NULL)
    g_object_unref (proxy->priv->connection);
226 227
  g_free (proxy->priv->name);
  g_free (proxy->priv->name_owner);
228 229 230 231 232 233
  g_free (proxy->priv->object_path);
  g_free (proxy->priv->interface_name);
  if (proxy->priv->properties != NULL)
    g_hash_table_unref (proxy->priv->properties);

  if (proxy->priv->expected_interface != NULL)
234 235 236 237
    {
      g_dbus_interface_info_cache_release (proxy->priv->expected_interface);
      g_dbus_interface_info_unref (proxy->priv->expected_interface);
    }
238

239 240 241
  if (proxy->priv->object != NULL)
    g_object_remove_weak_pointer (G_OBJECT (proxy->priv->object), (gpointer *) &proxy->priv->object);

Matthias Clasen's avatar
Matthias Clasen committed
242
  G_OBJECT_CLASS (g_dbus_proxy_parent_class)->finalize (object);
243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262
}

static void
g_dbus_proxy_get_property (GObject    *object,
                           guint       prop_id,
                           GValue     *value,
                           GParamSpec *pspec)
{
  GDBusProxy *proxy = G_DBUS_PROXY (object);

  switch (prop_id)
    {
    case PROP_G_CONNECTION:
      g_value_set_object (value, proxy->priv->connection);
      break;

    case PROP_G_FLAGS:
      g_value_set_flags (value, proxy->priv->flags);
      break;

263 264 265 266 267 268
    case PROP_G_NAME:
      g_value_set_string (value, proxy->priv->name);
      break;

    case PROP_G_NAME_OWNER:
      g_value_set_string (value, proxy->priv->name_owner);
269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310
      break;

    case PROP_G_OBJECT_PATH:
      g_value_set_string (value, proxy->priv->object_path);
      break;

    case PROP_G_INTERFACE_NAME:
      g_value_set_string (value, proxy->priv->interface_name);
      break;

    case PROP_G_DEFAULT_TIMEOUT:
      g_value_set_int (value, proxy->priv->timeout_msec);
      break;

    case PROP_G_INTERFACE_INFO:
      g_value_set_boxed (value, g_dbus_proxy_get_interface_info (proxy));
      break;

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
}

static void
g_dbus_proxy_set_property (GObject      *object,
                           guint         prop_id,
                           const GValue *value,
                           GParamSpec   *pspec)
{
  GDBusProxy *proxy = G_DBUS_PROXY (object);

  switch (prop_id)
    {
    case PROP_G_CONNECTION:
      proxy->priv->connection = g_value_dup_object (value);
      break;

    case PROP_G_FLAGS:
      proxy->priv->flags = g_value_get_flags (value);
      break;

311 312
    case PROP_G_NAME:
      proxy->priv->name = g_value_dup_string (value);
313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330
      break;

    case PROP_G_OBJECT_PATH:
      proxy->priv->object_path = g_value_dup_string (value);
      break;

    case PROP_G_INTERFACE_NAME:
      proxy->priv->interface_name = g_value_dup_string (value);
      break;

    case PROP_G_DEFAULT_TIMEOUT:
      g_dbus_proxy_set_default_timeout (proxy, g_value_get_int (value));
      break;

    case PROP_G_INTERFACE_INFO:
      g_dbus_proxy_set_interface_info (proxy, g_value_get_boxed (value));
      break;

331 332 333 334
    case PROP_G_BUS_TYPE:
      proxy->priv->bus_type = g_value_get_enum (value);
      break;

335 336 337 338 339 340 341 342 343 344 345
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
}

static void
g_dbus_proxy_class_init (GDBusProxyClass *klass)
{
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);

346
  gobject_class->dispose      = g_dbus_proxy_dispose;
347 348 349 350 351 352 353 354 355 356 357 358 359 360 361
  gobject_class->finalize     = g_dbus_proxy_finalize;
  gobject_class->set_property = g_dbus_proxy_set_property;
  gobject_class->get_property = g_dbus_proxy_get_property;

  /* Note that all property names are prefixed to avoid collisions with D-Bus property names
   * in derived classes */

  /**
   * GDBusProxy:g-interface-info:
   *
   * Ensure that interactions with this proxy conform to the given
   * interface.  For example, when completing a method call, if the
   * type signature of the message isn't what's expected, the given
   * #GError is set.  Signals that have a type signature mismatch are
   * simply dropped.
362 363
   *
   * Since: 2.26
364 365 366 367
   */
  g_object_class_install_property (gobject_class,
                                   PROP_G_INTERFACE_INFO,
                                   g_param_spec_boxed ("g-interface-info",
Matthias Clasen's avatar
Matthias Clasen committed
368 369
                                                       P_("Interface Information"),
                                                       P_("Interface Information"),
370 371 372 373 374 375 376 377 378 379 380
                                                       G_TYPE_DBUS_INTERFACE_INFO,
                                                       G_PARAM_READABLE |
                                                       G_PARAM_WRITABLE |
                                                       G_PARAM_STATIC_NAME |
                                                       G_PARAM_STATIC_BLURB |
                                                       G_PARAM_STATIC_NICK));

  /**
   * GDBusProxy:g-connection:
   *
   * The #GDBusConnection the proxy is for.
381 382
   *
   * Since: 2.26
383 384 385 386
   */
  g_object_class_install_property (gobject_class,
                                   PROP_G_CONNECTION,
                                   g_param_spec_object ("g-connection",
Matthias Clasen's avatar
Matthias Clasen committed
387 388
                                                        P_("g-connection"),
                                                        P_("The connection the proxy is for"),
389 390 391 392 393 394 395 396
                                                        G_TYPE_DBUS_CONNECTION,
                                                        G_PARAM_READABLE |
                                                        G_PARAM_WRITABLE |
                                                        G_PARAM_CONSTRUCT_ONLY |
                                                        G_PARAM_STATIC_NAME |
                                                        G_PARAM_STATIC_BLURB |
                                                        G_PARAM_STATIC_NICK));

397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419
  /**
   * GDBusProxy:g-bus-type:
   *
   * If this property is not %G_BUS_TYPE_NONE, then
   * #GDBusProxy:g-connection must be %NULL and will be set to the
   * #GDBusConnection obtained by calling g_bus_get() with the value
   * of this property.
   *
   * Since: 2.26
   */
  g_object_class_install_property (gobject_class,
                                   PROP_G_BUS_TYPE,
                                   g_param_spec_enum ("g-bus-type",
                                                      P_("Bus Type"),
                                                      P_("The bus to connect to, if any"),
                                                      G_TYPE_BUS_TYPE,
                                                      G_BUS_TYPE_NONE,
                                                      G_PARAM_WRITABLE |
                                                      G_PARAM_CONSTRUCT_ONLY |
                                                      G_PARAM_STATIC_NAME |
                                                      G_PARAM_STATIC_BLURB |
                                                      G_PARAM_STATIC_NICK));

420 421 422 423
  /**
   * GDBusProxy:g-flags:
   *
   * Flags from the #GDBusProxyFlags enumeration.
424 425
   *
   * Since: 2.26
426 427 428 429
   */
  g_object_class_install_property (gobject_class,
                                   PROP_G_FLAGS,
                                   g_param_spec_flags ("g-flags",
Matthias Clasen's avatar
Matthias Clasen committed
430 431
                                                       P_("g-flags"),
                                                       P_("Flags for the proxy"),
432 433 434 435 436 437 438 439 440 441
                                                       G_TYPE_DBUS_PROXY_FLAGS,
                                                       G_DBUS_PROXY_FLAGS_NONE,
                                                       G_PARAM_READABLE |
                                                       G_PARAM_WRITABLE |
                                                       G_PARAM_CONSTRUCT_ONLY |
                                                       G_PARAM_STATIC_NAME |
                                                       G_PARAM_STATIC_BLURB |
                                                       G_PARAM_STATIC_NICK));

  /**
442
   * GDBusProxy:g-name:
443
   *
444
   * The well-known or unique name that the proxy is for.
445 446
   *
   * Since: 2.26
447 448
   */
  g_object_class_install_property (gobject_class,
449 450 451 452
                                   PROP_G_NAME,
                                   g_param_spec_string ("g-name",
                                                        P_("g-name"),
                                                        P_("The well-known or unique name that the proxy is for"),
453 454 455 456 457 458 459 460
                                                        NULL,
                                                        G_PARAM_READABLE |
                                                        G_PARAM_WRITABLE |
                                                        G_PARAM_CONSTRUCT_ONLY |
                                                        G_PARAM_STATIC_NAME |
                                                        G_PARAM_STATIC_BLURB |
                                                        G_PARAM_STATIC_NICK));

461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480
  /**
   * GDBusProxy:g-name-owner:
   *
   * The unique name that owns #GDBusProxy:name or %NULL if no-one
   * currently owns that name. You may connect to #GObject::notify signal to
   * track changes to this property.
   *
   * Since: 2.26
   */
  g_object_class_install_property (gobject_class,
                                   PROP_G_NAME_OWNER,
                                   g_param_spec_string ("g-name-owner",
                                                        P_("g-name-owner"),
                                                        P_("The unique name for the owner"),
                                                        NULL,
                                                        G_PARAM_READABLE |
                                                        G_PARAM_STATIC_NAME |
                                                        G_PARAM_STATIC_BLURB |
                                                        G_PARAM_STATIC_NICK));

481 482 483 484
  /**
   * GDBusProxy:g-object-path:
   *
   * The object path the proxy is for.
485 486
   *
   * Since: 2.26
487 488 489 490
   */
  g_object_class_install_property (gobject_class,
                                   PROP_G_OBJECT_PATH,
                                   g_param_spec_string ("g-object-path",
Matthias Clasen's avatar
Matthias Clasen committed
491 492
                                                        P_("g-object-path"),
                                                        P_("The object path the proxy is for"),
493 494 495 496 497 498 499 500 501 502 503 504
                                                        NULL,
                                                        G_PARAM_READABLE |
                                                        G_PARAM_WRITABLE |
                                                        G_PARAM_CONSTRUCT_ONLY |
                                                        G_PARAM_STATIC_NAME |
                                                        G_PARAM_STATIC_BLURB |
                                                        G_PARAM_STATIC_NICK));

  /**
   * GDBusProxy:g-interface-name:
   *
   * The D-Bus interface name the proxy is for.
505 506
   *
   * Since: 2.26
507 508 509 510
   */
  g_object_class_install_property (gobject_class,
                                   PROP_G_INTERFACE_NAME,
                                   g_param_spec_string ("g-interface-name",
Matthias Clasen's avatar
Matthias Clasen committed
511 512
                                                        P_("g-interface-name"),
                                                        P_("The D-Bus interface name the proxy is for"),
513 514 515 516 517 518 519 520 521 522 523 524
                                                        NULL,
                                                        G_PARAM_READABLE |
                                                        G_PARAM_WRITABLE |
                                                        G_PARAM_CONSTRUCT_ONLY |
                                                        G_PARAM_STATIC_NAME |
                                                        G_PARAM_STATIC_BLURB |
                                                        G_PARAM_STATIC_NICK));

  /**
   * GDBusProxy:g-default-timeout:
   *
   * The timeout to use if -1 (specifying default timeout) is passed
525 526
   * as @timeout_msec in the g_dbus_proxy_call() and
   * g_dbus_proxy_call_sync() functions.
527 528 529 530 531
   *
   * This allows applications to set a proxy-wide timeout for all
   * remote method invocations on the proxy. If this property is -1,
   * the default timeout (typically 25 seconds) is used. If set to
   * %G_MAXINT, then no timeout is used.
532 533
   *
   * Since: 2.26
534 535 536 537
   */
  g_object_class_install_property (gobject_class,
                                   PROP_G_DEFAULT_TIMEOUT,
                                   g_param_spec_int ("g-default-timeout",
Matthias Clasen's avatar
Matthias Clasen committed
538 539
                                                     P_("Default Timeout"),
                                                     P_("Timeout for remote method invocation"),
540 541 542 543 544 545 546 547 548 549 550 551 552
                                                     -1,
                                                     G_MAXINT,
                                                     -1,
                                                     G_PARAM_READABLE |
                                                     G_PARAM_WRITABLE |
                                                     G_PARAM_CONSTRUCT |
                                                     G_PARAM_STATIC_NAME |
                                                     G_PARAM_STATIC_BLURB |
                                                     G_PARAM_STATIC_NICK));

  /**
   * GDBusProxy::g-properties-changed:
   * @proxy: The #GDBusProxy emitting the signal.
553 554
   * @changed_properties: A #GVariant containing the properties that changed
   * @invalidated_properties: A %NULL terminated array of properties that was invalidated
555
   *
556
   * Emitted when one or more D-Bus properties on @proxy changes. The
557 558 559
   * local cache has already been updated when this signal fires. Note
   * that both @changed_properties and @invalidated_properties are
   * guaranteed to never be %NULL (either may be empty though).
560 561 562 563
   *
   * This signal corresponds to the
   * <literal>PropertiesChanged</literal> D-Bus signal on the
   * <literal>org.freedesktop.DBus.Properties</literal> interface.
564 565
   *
   * Since: 2.26
566 567 568
   */
  signals[PROPERTIES_CHANGED_SIGNAL] = g_signal_new ("g-properties-changed",
                                                     G_TYPE_DBUS_PROXY,
569
                                                     G_SIGNAL_RUN_LAST | G_SIGNAL_MUST_COLLECT,
570 571 572
                                                     G_STRUCT_OFFSET (GDBusProxyClass, g_properties_changed),
                                                     NULL,
                                                     NULL,
573
                                                     NULL,
574
                                                     G_TYPE_NONE,
575 576
                                                     2,
                                                     G_TYPE_VARIANT,
577
                                                     G_TYPE_STRV | G_SIGNAL_TYPE_STATIC_SCOPE);
578 579 580 581 582 583 584 585 586

  /**
   * GDBusProxy::g-signal:
   * @proxy: The #GDBusProxy emitting the signal.
   * @sender_name: The sender of the signal or %NULL if the connection is not a bus connection.
   * @signal_name: The name of the signal.
   * @parameters: A #GVariant tuple with parameters for the signal.
   *
   * Emitted when a signal from the remote object and interface that @proxy is for, has been received.
587 588 589
   *
   * Since: 2.26
   */
590 591
  signals[SIGNAL_SIGNAL] = g_signal_new ("g-signal",
                                         G_TYPE_DBUS_PROXY,
592
                                         G_SIGNAL_RUN_LAST | G_SIGNAL_MUST_COLLECT,
593 594 595
                                         G_STRUCT_OFFSET (GDBusProxyClass, g_signal),
                                         NULL,
                                         NULL,
596
                                         NULL,
597 598 599 600 601 602 603 604 605 606 607 608 609 610
                                         G_TYPE_NONE,
                                         3,
                                         G_TYPE_STRING,
                                         G_TYPE_STRING,
                                         G_TYPE_VARIANT);


  g_type_class_add_private (klass, sizeof (GDBusProxyPrivate));
}

static void
g_dbus_proxy_init (GDBusProxy *proxy)
{
  proxy->priv = G_TYPE_INSTANCE_GET_PRIVATE (proxy, G_TYPE_DBUS_PROXY, GDBusProxyPrivate);
611 612 613
  proxy->priv->signal_subscription_data = g_slice_new0 (SignalSubscriptionData);
  proxy->priv->signal_subscription_data->ref_count = 1;
  proxy->priv->signal_subscription_data->proxy = proxy;
614 615 616 617
  proxy->priv->properties = g_hash_table_new_full (g_str_hash,
                                                   g_str_equal,
                                                   g_free,
                                                   (GDestroyNotify) g_variant_unref);
618 619 620 621
}

/* ---------------------------------------------------------------------------------------------------- */

622 623 624 625 626 627 628
static gint
property_name_sort_func (const gchar **a,
                         const gchar **b)
{
  return g_strcmp0 (*a, *b);
}

629 630 631 632 633 634
/**
 * g_dbus_proxy_get_cached_property_names:
 * @proxy: A #GDBusProxy.
 *
 * Gets the names of all cached properties on @proxy.
 *
635 636 637
 * Returns: (transfer full): A %NULL-terminated array of strings or %NULL if
 *          @proxy has no cached properties. Free the returned array with
 *          g_strfreev().
638 639
 *
 * Since: 2.26
640 641
 */
gchar **
642
g_dbus_proxy_get_cached_property_names (GDBusProxy  *proxy)
643 644 645 646 647 648 649 650
{
  gchar **names;
  GPtrArray *p;
  GHashTableIter iter;
  const gchar *key;

  g_return_val_if_fail (G_IS_DBUS_PROXY (proxy), NULL);

651 652
  G_LOCK (properties_lock);

653
  names = NULL;
654 655
  if (g_hash_table_size (proxy->priv->properties) == 0)
    goto out;
656 657 658 659 660

  p = g_ptr_array_new ();

  g_hash_table_iter_init (&iter, proxy->priv->properties);
  while (g_hash_table_iter_next (&iter, (gpointer) &key, NULL))
Matthias Clasen's avatar
Matthias Clasen committed
661
    g_ptr_array_add (p, g_strdup (key));
662
  g_ptr_array_sort (p, (GCompareFunc) property_name_sort_func);
663 664 665 666 667
  g_ptr_array_add (p, NULL);

  names = (gchar **) g_ptr_array_free (p, FALSE);

 out:
668
  G_UNLOCK (properties_lock);
669 670 671
  return names;
}

672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691
static const GDBusPropertyInfo *
lookup_property_info_or_warn (GDBusProxy  *proxy,
                              const gchar *property_name)
{
  const GDBusPropertyInfo *info;

  if (proxy->priv->expected_interface == NULL)
    return NULL;

  info = g_dbus_interface_info_lookup_property (proxy->priv->expected_interface, property_name);
  if (info == NULL)
    {
      g_warning ("Trying to lookup property %s which isn't in expected interface %s",
                 property_name,
                 proxy->priv->expected_interface->name);
    }

  return info;
}

692 693 694 695 696
/**
 * g_dbus_proxy_get_cached_property:
 * @proxy: A #GDBusProxy.
 * @property_name: Property name.
 *
697 698
 * Looks up the value for a property from the cache. This call does no
 * blocking IO.
699
 *
700 701 702
 * If @proxy has an expected interface (see
 * #GDBusProxy:g-interface-info), then @property_name (for existence)
 * is checked against it.
703
 *
704 705 706
 * Returns: A reference to the #GVariant instance that holds the value
 * for @property_name or %NULL if the value is not in the cache. The
 * returned reference must be freed with g_variant_unref().
707 708
 *
 * Since: 2.26
709 710
 */
GVariant *
Matthias Clasen's avatar
Matthias Clasen committed
711
g_dbus_proxy_get_cached_property (GDBusProxy   *proxy,
712
                                  const gchar  *property_name)
713 714 715 716 717 718
{
  GVariant *value;

  g_return_val_if_fail (G_IS_DBUS_PROXY (proxy), NULL);
  g_return_val_if_fail (property_name != NULL, NULL);

719 720
  G_LOCK (properties_lock);

721 722 723
  value = g_hash_table_lookup (proxy->priv->properties, property_name);
  if (value == NULL)
    {
724
      lookup_property_info_or_warn (proxy, property_name);
725
      /* no difference */
726 727 728 729 730 731
      goto out;
    }

  g_variant_ref (value);

 out:
732
  G_UNLOCK (properties_lock);
733 734 735
  return value;
}

736 737 738 739
/**
 * g_dbus_proxy_set_cached_property:
 * @proxy: A #GDBusProxy
 * @property_name: Property name.
740
 * @value: (allow-none): Value for the property or %NULL to remove it from the cache.
741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788
 *
 * If @value is not %NULL, sets the cached value for the property with
 * name @property_name to the value in @value.
 *
 * If @value is %NULL, then the cached value is removed from the
 * property cache.
 *
 * If @proxy has an expected interface (see
 * #GDBusProxy:g-interface-info), then @property_name (for existence)
 * and @value (for the type) is checked against it.
 *
 * If the @value #GVariant is floating, it is consumed. This allows
 * convenient 'inline' use of g_variant_new(), e.g.
 * |[
 *  g_dbus_proxy_set_cached_property (proxy,
 *                                    "SomeProperty",
 *                                    g_variant_new ("(si)",
 *                                                  "A String",
 *                                                  42));
 * ]|
 *
 * Normally you will not need to use this method since @proxy is
 * tracking changes using the
 * <literal>org.freedesktop.DBus.Properties.PropertiesChanged</literal>
 * D-Bus signal. However, for performance reasons an object may decide
 * to not use this signal for some properties and instead use a
 * proprietary out-of-band mechanism to transmit changes.
 *
 * As a concrete example, consider an object with a property
 * <literal>ChatroomParticipants</literal> which is an array of
 * strings. Instead of transmitting the same (long) array every time
 * the property changes, it is more efficient to only transmit the
 * delta using e.g. signals <literal>ChatroomParticipantJoined(String
 * name)</literal> and <literal>ChatroomParticipantParted(String
 * name)</literal>.
 *
 * Since: 2.26
 */
void
g_dbus_proxy_set_cached_property (GDBusProxy   *proxy,
                                  const gchar  *property_name,
                                  GVariant     *value)
{
  const GDBusPropertyInfo *info;

  g_return_if_fail (G_IS_DBUS_PROXY (proxy));
  g_return_if_fail (property_name != NULL);

789 790
  G_LOCK (properties_lock);

791 792 793 794 795 796 797
  if (value != NULL)
    {
      info = lookup_property_info_or_warn (proxy, property_name);
      if (info != NULL)
        {
          if (g_strcmp0 (info->signature, g_variant_get_type_string (value)) != 0)
            {
798 799
              g_warning ("Trying to set property %s of type %s but according to the expected "
			 "interface the type is %s",
800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815
                         property_name,
                         g_variant_get_type_string (value),
                         info->signature);
              goto out;
            }
        }
      g_hash_table_insert (proxy->priv->properties,
                           g_strdup (property_name),
                           g_variant_ref_sink (value));
    }
  else
    {
      g_hash_table_remove (proxy->priv->properties, property_name);
    }

 out:
816
  G_UNLOCK (properties_lock);
817 818
}

819 820 821
/* ---------------------------------------------------------------------------------------------------- */

static void
Matthias Clasen's avatar
Matthias Clasen committed
822 823 824 825 826 827 828
on_signal_received (GDBusConnection *connection,
                    const gchar     *sender_name,
                    const gchar     *object_path,
                    const gchar     *interface_name,
                    const gchar     *signal_name,
                    GVariant        *parameters,
                    gpointer         user_data)
829
{
830 831 832 833 834 835
  SignalSubscriptionData *data = user_data;
  GDBusProxy *proxy;

  G_LOCK (signal_subscription_lock);
  proxy = data->proxy;
  if (proxy == NULL)
836 837 838 839 840 841 842 843 844
    {
      G_UNLOCK (signal_subscription_lock);
      goto out;
    }
  else
    {
      g_object_ref (proxy);
      G_UNLOCK (signal_subscription_lock);
    }
845

846 847 848
  if (!proxy->priv->initialized)
    goto out;

849
  if (proxy->priv->name_owner != NULL && g_strcmp0 (sender_name, proxy->priv->name_owner) != 0)
850 851
    goto out;

852 853 854
  if (proxy->priv->expected_interface != NULL)
    {
      const GDBusSignalInfo *info;
855
      GVariantType *expected_type;
856 857 858
      info = g_dbus_interface_info_lookup_signal (proxy->priv->expected_interface, signal_name);
      if (info == NULL)
        goto out;
859 860 861 862 863 864 865
      expected_type = _g_dbus_compute_complete_signature (info->args);
      if (!g_variant_type_equal (expected_type, g_variant_get_type (parameters)))
        {
          g_variant_type_free (expected_type);
          goto out;
        }
      g_variant_type_free (expected_type);
866 867
    }

868 869 870 871 872 873
  g_signal_emit (proxy,
                 signals[SIGNAL_SIGNAL],
                 0,
                 sender_name,
                 signal_name,
                 parameters);
874
 out:
875 876
  if (proxy != NULL)
    g_object_unref (proxy);
877 878 879 880
}

/* ---------------------------------------------------------------------------------------------------- */

881
/* must hold properties_lock */
882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911
static void
insert_property_checked (GDBusProxy  *proxy,
			 gchar *property_name,
			 GVariant *value)
{
  if (proxy->priv->expected_interface != NULL)
    {
      const GDBusPropertyInfo *info;

      info = g_dbus_interface_info_lookup_property (proxy->priv->expected_interface, property_name);
      /* Ignore unknown properties */
      if (info == NULL)
	goto invalid;

      /* Ignore properties with the wrong type */
      if (g_strcmp0 (info->signature, g_variant_get_type_string (value)) != 0)
	goto invalid;
    }

  g_hash_table_insert (proxy->priv->properties,
		       property_name, /* adopts string */
		       value); /* adopts value */

  return;

 invalid:
  g_variant_unref (value);
  g_free (property_name);
}

912
static void
Matthias Clasen's avatar
Matthias Clasen committed
913 914 915 916 917 918 919
on_properties_changed (GDBusConnection *connection,
                       const gchar     *sender_name,
                       const gchar     *object_path,
                       const gchar     *interface_name,
                       const gchar     *signal_name,
                       GVariant        *parameters,
                       gpointer         user_data)
920
{
921 922
  SignalSubscriptionData *data = user_data;
  GDBusProxy *proxy;
923
  const gchar *interface_name_for_signal;
924
  GVariant *changed_properties;
925
  gchar **invalidated_properties;
926 927 928
  GVariantIter iter;
  gchar *key;
  GVariant *value;
929
  guint n;
930

931 932 933
  changed_properties = NULL;
  invalidated_properties = NULL;

934 935 936
  G_LOCK (signal_subscription_lock);
  proxy = data->proxy;
  if (proxy == NULL)
937 938 939 940 941 942 943 944 945
    {
      G_UNLOCK (signal_subscription_lock);
      goto out;
    }
  else
    {
      g_object_ref (proxy);
      G_UNLOCK (signal_subscription_lock);
    }
946

947 948
  if (!proxy->priv->initialized)
    goto out;
949

950
  if (proxy->priv->name_owner != NULL && g_strcmp0 (sender_name, proxy->priv->name_owner) != 0)
951 952
    goto out;

953
  if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(sa{sv}as)")))
954
    {
955
      g_warning ("Value for PropertiesChanged signal with type `%s' does not match `(sa{sv}as)'",
956
                 g_variant_get_type_string (parameters));
957
      goto out;
958 959 960
    }

  g_variant_get (parameters,
961
                 "(&s@a{sv}^a&s)",
962
                 &interface_name_for_signal,
963 964
                 &changed_properties,
                 &invalidated_properties);
965 966 967 968

  if (g_strcmp0 (interface_name_for_signal, proxy->priv->interface_name) != 0)
    goto out;

969 970
  G_LOCK (properties_lock);

971 972
  g_variant_iter_init (&iter, changed_properties);
  while (g_variant_iter_next (&iter, "{sv}", &key, &value))
973
    {
974 975 976
      insert_property_checked (proxy,
			       key, /* adopts string */
			       value); /* adopts value */
977 978
    }

979 980 981 982 983
  for (n = 0; invalidated_properties[n] != NULL; n++)
    {
      g_hash_table_remove (proxy->priv->properties, invalidated_properties[n]);
    }

984 985
  G_UNLOCK (properties_lock);

986
  /* emit signal */
987 988 989 990
  g_signal_emit (proxy, signals[PROPERTIES_CHANGED_SIGNAL],
                 0,
                 changed_properties,
                 invalidated_properties);
991 992

 out:
993 994
  if (changed_properties != NULL)
    g_variant_unref (changed_properties);
995
  g_free (invalidated_properties);
996 997
  if (proxy != NULL)
    g_object_unref (proxy);
998 999 1000 1001 1002 1003 1004 1005
}

/* ---------------------------------------------------------------------------------------------------- */

static void
process_get_all_reply (GDBusProxy *proxy,
                       GVariant   *result)
{
Christian Persch's avatar
Christian Persch committed
1006 1007 1008
  GVariantIter *iter;
  gchar *key;
  GVariant *value;
1009
  guint num_properties;
1010

1011
  if (!g_variant_is_of_type (result, G_VARIANT_TYPE ("(a{sv})")))
1012 1013 1014 1015 1016 1017
    {
      g_warning ("Value for GetAll reply with type `%s' does not match `(a{sv})'",
                 g_variant_get_type_string (result));
      goto out;
    }

1018 1019
  G_LOCK (properties_lock);

Christian Persch's avatar
Christian Persch committed
1020 1021
  g_variant_get (result, "(a{sv})", &iter);
  while (g_variant_iter_next (iter, "{sv}", &key, &value))
1022
    {
1023 1024 1025
      insert_property_checked (proxy,
			       key, /* adopts string */
			       value); /* adopts value */
1026
    }
Christian Persch's avatar
Christian Persch committed
1027 1028
  g_variant_iter_free (iter);

1029 1030 1031
  num_properties = g_hash_table_size (proxy->priv->properties);
  G_UNLOCK (properties_lock);

1032
  /* Synthesize ::g-properties-changed changed */
1033
  if (num_properties > 0)
1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047
    {
      GVariant *changed_properties;
      const gchar *invalidated_properties[1] = {NULL};

      g_variant_get (result,
                     "(@a{sv})",
                     &changed_properties);
      g_signal_emit (proxy, signals[PROPERTIES_CHANGED_SIGNAL],
                     0,
                     changed_properties,
                     invalidated_properties);
      g_variant_unref (changed_properties);
    }

1048 1049 1050 1051
 out:
  ;
}

1052
typedef struct
1053
{
1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064
  GDBusProxy *proxy;
  GCancellable *cancellable;
  gchar *name_owner;
} LoadPropertiesOnNameOwnerChangedData;

static void
on_name_owner_changed_get_all_cb (GDBusConnection *connection,
                                  GAsyncResult    *res,
                                  gpointer         user_data)
{
  LoadPropertiesOnNameOwnerChangedData *data = user_data;
1065
  GVariant *result;
1066 1067
  GError *error;
  gboolean cancelled;
1068

1069
  cancelled = FALSE;
1070

1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091
  error = NULL;
  result = g_dbus_connection_call_finish (connection,
                                          res,
                                          &error);
  if (result == NULL)
    {
      if (error->domain == G_IO_ERROR && error->code == G_IO_ERROR_CANCELLED)
        cancelled = TRUE;
      /* We just ignore if GetAll() is failing. Because this might happen
       * if the object has no properties at all. Or if the caller is
       * not authorized to see the properties.
       *
       * Either way, apps can know about this by using
       * get_cached_property_names() or get_cached_property().
       *
       * TODO: handle G_DBUS_DEBUG flag 'proxy' and, if enabled, log the
       * fact that GetAll() failed
       */
      //g_debug ("error: %d %d %s", error->domain, error->code, error->message);
      g_error_free (error);
    }
1092

1093 1094
  /* and finally we can notify */
  if (!cancelled)
1095
    {
1096 1097 1098 1099
      g_free (data->proxy->priv->name_owner);
      data->proxy->priv->name_owner = data->name_owner;
      data->name_owner = NULL; /* to avoid an extra copy, we steal the string */

1100
      G_LOCK (properties_lock);
1101
      g_hash_table_remove_all (data->proxy->priv->properties);
1102
      G_UNLOCK (properties_lock);
1103
      if (result != NULL)
1104
        {
1105 1106
          process_get_all_reply (data->proxy, result);
          g_variant_unref (result);
1107
        }
1108

1109
      g_object_notify (G_OBJECT (data->proxy), "g-name-owner");
1110 1111
    }

1112 1113
  if (data->cancellable == data->proxy->priv->get_all_cancellable)
    data->proxy->priv->get_all_cancellable = NULL;
1114

1115 1116 1117 1118
  g_object_unref (data->proxy);
  g_object_unref (data->cancellable);
  g_free (data->name_owner);
  g_free (data);
1119 1120 1121
}

static void
1122 1123 1124 1125 1126 1127 1128
on_name_owner_changed (GDBusConnection *connection,
                       const gchar      *sender_name,
                       const gchar      *object_path,
                       const gchar      *interface_name,
                       const gchar      *signal_name,
                       GVariant         *parameters,
                       gpointer          user_data)
1129
{
1130 1131
  SignalSubscriptionData *data = user_data;
  GDBusProxy *proxy;
1132 1133 1134
  const gchar *old_owner;
  const gchar *new_owner;

1135 1136 1137
  G_LOCK (signal_subscription_lock);
  proxy = data->proxy;
  if (proxy == NULL)
1138 1139 1140 1141 1142 1143 1144 1145 1146
    {
      G_UNLOCK (signal_subscription_lock);
      goto out;
    }
  else
    {
      g_object_ref (proxy);
      G_UNLOCK (signal_subscription_lock);
    }
1147

1148 1149 1150 1151 1152 1153 1154 1155 1156
  /* if we are already trying to load properties, cancel that */
  if (proxy->priv->get_all_cancellable != NULL)
    {
      g_cancellable_cancel (proxy->priv->get_all_cancellable);
      proxy->priv->get_all_cancellable = NULL;
    }

  g_variant_get (parameters,
                 "(&s&s&s)",
1157
                 NULL,
1158 1159 1160 1161 1162 1163 1164
                 &old_owner,
                 &new_owner);

  if (strlen (new_owner) == 0)
    {
      g_free (proxy->priv->name_owner);
      proxy->priv->name_owner = NULL;
1165

1166 1167
      G_LOCK (properties_lock);

1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178
      /* Synthesize ::g-properties-changed changed */
      if (!(proxy->priv->flags & G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES) &&
          g_hash_table_size (proxy->priv->properties) > 0)
        {
          GVariantBuilder builder;
          GPtrArray *invalidated_properties;
          GHashTableIter iter;
          const gchar *key;

          /* Build changed_properties (always empty) and invalidated_properties ... */
          g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
1179

1180 1181 1182 1183 1184 1185 1186 1187 1188
          invalidated_properties = g_ptr_array_new_with_free_func (g_free);
          g_hash_table_iter_init (&iter, proxy->priv->properties);
          while (g_hash_table_iter_next (&iter, (gpointer) &key, NULL))
            g_ptr_array_add (invalidated_properties, g_strdup (key));
          g_ptr_array_add (invalidated_properties, NULL);

          /* ... throw out the properties ... */
          g_hash_table_remove_all (proxy->priv->properties);

1189 1190
          G_UNLOCK (properties_lock);

1191 1192 1193
          /* ... and finally emit the ::g-properties-changed signal */
          g_signal_emit (proxy, signals[PROPERTIES_CHANGED_SIGNAL],
                         0,
1194
                         g_variant_builder_end (&builder) /* consumed */,
1195 1196 1197
                         (const gchar* const *) invalidated_properties->pdata);
          g_ptr_array_unref (invalidated_properties);
        }
1198 1199 1200 1201
      else
        {
          G_UNLOCK (properties_lock);
        }
1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213
      g_object_notify (G_OBJECT (proxy), "g-name-owner");
    }
  else
    {
      /* ignore duplicates - this can happen when activating the service */
      if (g_strcmp0 (new_owner, proxy->priv->name_owner) == 0)
        goto out;

      if (proxy->priv->flags & G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES)
        {
          g_free (proxy->priv->name_owner);
          proxy->priv->name_owner = g_strdup (new_owner);
1214
          G_LOCK (properties_lock);
1215
          g_hash_table_remove_all (proxy->priv->properties);
1216
          G_UNLOCK (properties_lock);
1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249
          g_object_notify (G_OBJECT (proxy), "g-name-owner");
        }
      else
        {
          LoadPropertiesOnNameOwnerChangedData *data;

          /* start loading properties.. only then emit notify::g-name-owner .. we
           * need to be able to cancel this in the event another NameOwnerChanged
           * signal suddenly happens
           */

          g_assert (proxy->priv->get_all_cancellable == NULL);
          proxy->priv->get_all_cancellable = g_cancellable_new ();
          data = g_new0 (LoadPropertiesOnNameOwnerChangedData, 1);
          data->proxy = g_object_ref (proxy);
          data->cancellable = proxy->priv->get_all_cancellable;
          data->name_owner = g_strdup (new_owner);
          g_dbus_connection_call (proxy->priv->connection,
                                  data->name_owner,
                                  proxy->priv->object_path,
                                  "org.freedesktop.DBus.Properties",
                                  "GetAll",
                                  g_variant_new ("(s)", proxy->priv->interface_name),
                                  G_VARIANT_TYPE ("(a{sv})"),
                                  G_DBUS_CALL_FLAGS_NONE,
                                  -1,           /* timeout */
                                  proxy->priv->get_all_cancellable,
                                  (GAsyncReadyCallback) on_name_owner_changed_get_all_cb,
                                  data);
        }
    }

 out:
1250 1251
  if (proxy != NULL)
    g_object_unref (proxy);
1252 1253 1254 1255
}

/* ---------------------------------------------------------------------------------------------------- */

1256 1257 1258 1259 1260 1261 1262
typedef struct
{
  GDBusProxy *proxy;
  GCancellable *cancellable;
  GSimpleAsyncResult *simple;
} AsyncInitData;

1263
static void
1264
async_init_data_free (AsyncInitData *data)
1265
{
1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278
  g_object_unref (data->proxy);
  if (data->cancellable != NULL)
    g_object_unref (data->cancellable);
  g_object_unref (data->simple);
  g_free (data);
}

static void
async_init_get_all_cb (GDBusConnection *connection,
                       GAsyncResult    *res,
                       gpointer         user_data)
{
  AsyncInitData *data = user_data;
1279 1280 1281 1282
  GVariant *result;
  GError *error;

  error = NULL;
1283 1284 1285
  result = g_dbus_connection_call_finish (connection,
                                          res,
                                          &error);
1286 1287
  if (result == NULL)
    {
1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298
      /* We just ignore if GetAll() is failing. Because this might happen
       * if the object has no properties at all. Or if the caller is
       * not authorized to see the properties.
       *
       * Either way, apps can know about this by using
       * get_cached_property_names() or get_cached_property().
       *
       * TODO: handle G_DBUS_DEBUG flag 'proxy' and, if enabled, log the
       * fact that GetAll() failed
       */
      //g_debug ("error: %d %d %s", error->domain, error->code, error->message);
1299 1300 1301 1302
      g_error_free (error);
    }
  else
    {
1303
      g_simple_async_result_set_op_res_gpointer (data->simple,
1304 1305 1306 1307
                                                 result,
                                                 (GDestroyNotify) g_variant_unref);
    }

1308 1309
  g_simple_async_result_complete_in_idle (data->simple);
  async_init_data_free (data);
1310 1311
}

1312

1313
static void
1314 1315 1316
async_init_get_name_owner_cb (GDBusConnection *connection,
                              GAsyncResult    *res,
                              gpointer         user_data)
1317
{
1318
  AsyncInitData *data = user_data;
1319

1320 1321 1322 1323
  if (res != NULL)
    {
      GError *error;
      GVariant *result;
1324

1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337
      error = NULL;
      result = g_dbus_connection_call_finish (connection,
                                              res,
                                              &error);
      if (result == NULL)
        {
          if (error->domain == G_DBUS_ERROR &&
              error->code == G_DBUS_ERROR_NAME_HAS_NO_OWNER)
            {
              g_error_free (error);
            }
          else
            {
1338
              g_simple_async_result_take_error (data->simple, error);
1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351
              g_simple_async_result_complete_in_idle (data->simple);
              async_init_data_free (data);
              goto out;
            }
        }
      else
        {
          g_variant_get (result,
                         "(s)",
                         &data->proxy->priv->name_owner);
          g_variant_unref (result);
        }
    }
1352

1353
  if (!(data->proxy->priv->flags & G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES))
1354 1355
    {
      /* load all properties asynchronously */
1356 1357 1358
      g_dbus_connection_call (data->proxy->priv->connection,
                              data->proxy->priv->name_owner,
                              data->proxy->priv->object_path,
1359 1360
                              "org.freedesktop.DBus.Properties",
                              "GetAll",
1361
                              g_variant_new ("(s)", data->proxy->priv->interface_name),
1362
                              G_VARIANT_TYPE ("(a{sv})"),
1363 1364
                              G_DBUS_CALL_FLAGS_NONE,
                              -1,           /* timeout */
1365 1366 1367
                              data->cancellable,
                              (GAsyncReadyCallback) async_init_get_all_cb,
                              data);
1368 1369 1370
    }
  else
    {
1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461
      g_simple_async_result_complete_in_idle (data->simple);
      async_init_data_free (data);
    }

 out:
  ;
}

static void
async_init_call_get_name_owner (AsyncInitData *data)
{
  g_dbus_connection_call (data->proxy->priv->connection,
                          "org.freedesktop.DBus",  /* name */
                          "/org/freedesktop/DBus", /* object path */
                          "org.freedesktop.DBus",  /* interface */
                          "GetNameOwner",
                          g_variant_new ("(s)",
                                         data->proxy->priv->name),
                          G_VARIANT_TYPE ("(s)"),
                          G_DBUS_CALL_FLAGS_NONE,
                          -1,           /* timeout */
                          data->cancellable,
                          (GAsyncReadyCallback) async_init_get_name_owner_cb,
                          data);
}

static void
async_init_start_service_by_name_cb (GDBusConnection *connection,
                                     GAsyncResult    *res,
                                     gpointer         user_data)
{
  AsyncInitData *data = user_data;
  GError *error;
  GVariant *result;

  error = NULL;
  result = g_dbus_connection_call_finish (connection,
                                          res,
                                          &error);
  if (result == NULL)
    {
      /* Errors are not unexpected; the bus will reply e.g.
       *
       *   org.freedesktop.DBus.Error.ServiceUnknown: The name org.gnome.Epiphany2
       *   was not provided by any .service files
       *
       * This doesn't mean that the name doesn't have an owner, just
       * that it's not provided by a .service file. So just proceed to
       * invoke GetNameOwner() if dealing with that error.
       */
      if (error->domain == G_DBUS_ERROR &&
          error->code == G_DBUS_ERROR_SERVICE_UNKNOWN)
        {
          g_error_free (error);
        }
      else
        {
          g_prefix_error (&error,
                          _("Error calling StartServiceByName for %s: "),
                          data->proxy->priv->name);
          goto failed;
        }
    }
  else
    {
      guint32 start_service_result;
      g_variant_get (result,
                     "(u)",
                     &start_service_result);
      g_variant_unref (result);
      if (start_service_result == 1 ||  /* DBUS_START_REPLY_SUCCESS */
          start_service_result == 2)    /* DBUS_START_REPLY_ALREADY_RUNNING */
        {
          /* continue to invoke GetNameOwner() */
        }
      else
        {
          error = g_error_new (G_IO_ERROR,
                               G_IO_ERROR_FAILED,
                               _("Unexpected reply %d from StartServiceByName(\"%s\") method"),
                               start_service_result,
                               data->proxy->priv->name);
          goto failed;
        }
    }

  async_init_call_get_name_owner (data);
  return;

 failed:
  g_warn_if_fail (error != NULL);
1462
  g_simple_async_result_take_error (data->simple, error);
1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524
  g_simple_async_result_complete_in_idle (data->simple);
  async_init_data_free (data);
}

static void
async_init_call_start_service_by_name (AsyncInitData *data)
{
  g_dbus_connection_call (data->proxy->priv->connection,
                          "org.freedesktop.DBus",  /* name */
                          "/org/freedesktop/DBus", /* object path */
                          "org.freedesktop.DBus",  /* interface */
                          "StartServiceByName",
                          g_variant_new ("(su)",
                                         data->proxy->priv->name,
                                         0),
                          G_VARIANT_TYPE ("(u)"),
                          G_DBUS_CALL_FLAGS_NONE,
                          -1,           /* timeout */
                          data->cancellable,
                          (GAsyncReadyCallback) async_init_start_service_by_name_cb,
                          data);
}

static void
async_initable_init_second_async (GAsyncInitable      *initable,
                                  gint                 io_priority,
                                  GCancellable        *cancellable,
                                  GAsyncReadyCallback  callback,
                                  gpointer             user_data)
{
  GDBusProxy *proxy = G_DBUS_PROXY (initable);
  AsyncInitData *data;

  data = g_new0 (AsyncInitData, 1);
  data->proxy = g_object_ref (proxy);
  data->cancellable = cancellable != NULL ? g_object_ref (cancellable) : NULL;
  data->simple = g_simple_async_result_new (G_OBJECT (proxy),
                                            callback,
                                            user_data,
                                            NULL);

  /* Check name ownership asynchronously - possibly also start the service */
  if (proxy->priv->name == NULL)
    {
      /* Do nothing */
      async_init_get_name_owner_cb (proxy->priv->connection, NULL, data);
    }
  else if (g_dbus_is_unique_name (proxy->priv->name))
    {
      proxy->priv->name_owner = g_strdup (proxy->priv->name);
      async_init_get_name_owner_cb (proxy->priv->connection, NULL, data);
    }
  else
    {
      if (proxy->priv->flags & G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START)
        {
          async_init_call_get_name_owner (data);
        }
      else
        {
          async_init_call_start_service_by_name (data);
        }
1525 1526 1527 1528
    }
}

static gboolean
1529 1530 1531
async_initable_init_second_finish (GAsyncInitable  *initable,
                                   GAsyncResult    *res,
                                   GError         **error)
1532 1533 1534 1535 1536 1537 1538 1539
{
  GDBusProxy *proxy = G_DBUS_PROXY (initable);
  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
  GVariant *result;
  gboolean ret;

  ret = FALSE;

1540 1541 1542
  if (g_simple_async_result_propagate_error (simple, error))
    goto out;

1543
  result = g_simple_async_result_get_op_res_gpointer (simple);
1544
  if (result != NULL)
1545 1546 1547 1548 1549 1550 1551
    {
      process_get_all_reply (proxy, result);
    }

  ret = TRUE;

 out:
1552
  proxy->priv->initialized = TRUE;
1553 1554 1555
  return ret;
}

1556 1557 1558 1559 1560 1561 1562 1563 1564 1565
/* ---------------------------------------------------------------------------------------------------- */

static void
async_initable_init_first (GAsyncInitable *initable)
{
  GDBusProxy *proxy = G_DBUS_PROXY (initable);

  if (!(proxy->priv->flags & G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES))
    {
      /* subscribe to PropertiesChanged() */
1566
      proxy->priv->properties_changed_subscription_id =
1567 1568 1569 1570 1571 1572
        g_dbus_connection_signal_subscribe (proxy->priv->connection,
                                            proxy->priv->name,
                                            "org.freedesktop.DBus.Properties",
                                            "PropertiesChanged",
                                            proxy->priv->object_path,
                                            proxy->priv->interface_name,
1573
                                            G_DBUS_SIGNAL_FLAGS_NONE,
1574
                                            on_properties_changed,
1575 1576
                                            signal_subscription_ref (proxy->priv->signal_subscription_data),
                                            (GDestroyNotify) signal_subscription_unref);
1577 1578 1579 1580 1581
    }

  if (!(proxy->priv->flags & G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS))
    {
      /* subscribe to all signals for the object */
1582
      proxy->priv->signals_subscription_id =
1583 1584 1585 1586 1587 1588
        g_dbus_connection_signal_subscribe (proxy->priv->connection,
                                            proxy->priv->name,
                                            proxy->priv->interface_name,
                                            NULL,                        /* member */
                                            proxy->priv->object_path,
                                            NULL,                        /* arg0 */
1589
                                            G_DBUS_SIGNAL_FLAGS_NONE,
1590
                                            on_signal_received,
1591 1592
                                            signal_subscription_ref (proxy->priv->signal_subscription_data),
                                            (GDestroyNotify) signal_subscription_unref);
1593 1594 1595 1596 1597 1598 1599