GDBusProxy: Check if connection is NULL before unreffing
[platform/upstream/glib.git] / gio / gdbusproxy.c
index 65ea484..06957f6 100644 (file)
  * 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
- * changes emitted from the current name owner are considered. This
- * avoids a number of race conditions when the name is lost by one
- * owner and claimed by another.
+ * 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).
  *
  * The generic #GDBusProxy::g-properties-changed and #GDBusProxy::g-signal
  * signals are not very convenient to work with. Therefore, the recommended
@@ -152,7 +156,8 @@ g_dbus_proxy_finalize (GObject *object)
     g_dbus_connection_signal_unsubscribe (proxy->priv->connection,
                                           proxy->priv->signals_subscriber_id);
 
-  g_object_unref (proxy->priv->connection);
+  if (proxy->priv->connection != NULL)
+    g_object_unref (proxy->priv->connection);
   g_free (proxy->priv->name);
   g_free (proxy->priv->name_owner);
   g_free (proxy->priv->object_path);
@@ -1136,8 +1141,7 @@ async_init_get_name_owner_cb (GDBusConnection *connection,
             }
           else
             {
-              g_simple_async_result_set_from_error (data->simple, error);
-              g_error_free (error);
+              g_simple_async_result_take_error (data->simple, error);
               g_simple_async_result_complete_in_idle (data->simple);
               async_init_data_free (data);
               goto out;
@@ -1261,8 +1265,7 @@ async_init_start_service_by_name_cb (GDBusConnection *connection,
 
  failed:
   g_warn_if_fail (error != NULL);
-  g_simple_async_result_set_from_error (data->simple, error);
-  g_error_free (error);
+  g_simple_async_result_take_error (data->simple, error);
   g_simple_async_result_complete_in_idle (data->simple);
   async_init_data_free (data);
 }
@@ -1449,8 +1452,7 @@ get_connection_cb (GObject       *source_object,
                                           data->callback,
                                           data->user_data,
                                           NULL);
-      g_simple_async_result_set_from_error (simple, error);
-      g_error_free (error);
+      g_simple_async_result_take_error (simple, error);
       g_simple_async_result_complete_in_idle (simple);
       g_object_unref (simple);
     }
@@ -1622,16 +1624,16 @@ initable_iface_init (GInitableIface *initable_iface)
  * Creates a proxy for accessing @interface_name on the remote object
  * at @object_path owned by @name at @connection and asynchronously
  * loads D-Bus properties unless the
- * #G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES flag is used. Connect to
+ * %G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES flag is used. Connect to
  * the #GDBusProxy::g-properties-changed signal to get notified about
  * property changes.
  *
- * If the #G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS flag is not set, also sets up
+ * If the %G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS flag is not set, also sets up
  * match rules for signals. Connect to the #GDBusProxy::g-signal signal
  * to handle signals from the remote object.
  *
  * If @name is a well-known name and the
- * #G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START flag isn't set and no name
+ * %G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START flag isn't set and no name
  * owner currently exists, the message bus will be requested to launch
  * a name owner for the name.
  *
@@ -1721,14 +1723,14 @@ g_dbus_proxy_new_finish (GAsyncResult  *res,
  * Creates a proxy for accessing @interface_name on the remote object
  * at @object_path owned by @name at @connection and synchronously
  * loads D-Bus properties unless the
- * #G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES flag is used.
+ * %G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES flag is used.
  *
- * If the #G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS flag is not set, also sets up
+ * If the %G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS flag is not set, also sets up
  * match rules for signals. Connect to the #GDBusProxy::g-signal signal
  * to handle signals from the remote object.
  *
  * If @name is a well-known name and the
- * #G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START flag isn't set and no name
+ * %G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START flag isn't set and no name
  * owner currently exists, the message bus will be requested to launch
  * a name owner for the name.
  *
@@ -1901,7 +1903,7 @@ g_dbus_proxy_new_for_bus_sync (GBusType             bus_type,
  *
  * Gets the connection @proxy is for.
  *
- * Returns: A #GDBusConnection owned by @proxy. Do not free.
+ * Returns: (transfer none): A #GDBusConnection owned by @proxy. Do not free.
  *
  * Since: 2.26
  */
@@ -2144,9 +2146,7 @@ reply_cb (GDBusConnection *connection,
                                          &error);
   if (error != NULL)
     {
-      g_simple_async_result_set_from_error (simple,
-                                            error);
-      g_error_free (error);
+      g_simple_async_result_take_error (simple, error);
     }
   else
     {
@@ -2179,13 +2179,39 @@ lookup_method_info_or_warn (GDBusProxy  *proxy,
   return info;
 }
 
+static const gchar *
+get_destination_for_call (GDBusProxy *proxy)
+{
+  const gchar *ret;
+
+  ret = NULL;
+
+  /* If proxy->priv->name is a unique name, then proxy->priv->name_owner
+   * is never NULL and always the same as proxy->priv->name. We use this
+   * knowledge to avoid checking if proxy->priv->name is a unique or
+   * well-known name.
+   */
+  ret = proxy->priv->name_owner;
+  if (ret != NULL)
+    goto out;
+
+  if (proxy->priv->flags & G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START)
+    goto out;
+
+  ret = proxy->priv->name;
+
+ out:
+  return ret;
+}
+
 /**
  * g_dbus_proxy_call:
  * @proxy: A #GDBusProxy.
  * @method_name: Name of method to invoke.
  * @parameters: A #GVariant tuple with parameters for the signal or %NULL if not passing parameters.
  * @flags: Flags from the #GDBusCallFlags enumeration.
- * @timeout_msec: The timeout in milliseconds or -1 to use the proxy default timeout.
+ * @timeout_msec: The timeout in milliseconds (with %G_MAXINT meaning
+ *                "infinite") or -1 to use the proxy default timeout.
  * @cancellable: A #GCancellable or %NULL.
  * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL if you don't
  * care about the result of the method invocation.
@@ -2243,9 +2269,9 @@ g_dbus_proxy_call (GDBusProxy          *proxy,
   gboolean was_split;
   gchar *split_interface_name;
   const gchar *split_method_name;
-  const GDBusMethodInfo *expected_method_info;
   const gchar *target_method_name;
   const gchar *target_interface_name;
+  const gchar *destination;
   GVariantType *reply_type;
 
   g_return_if_fail (G_IS_DBUS_PROXY (proxy));
@@ -2253,6 +2279,9 @@ g_dbus_proxy_call (GDBusProxy          *proxy,
   g_return_if_fail (parameters == NULL || g_variant_is_of_type (parameters, G_VARIANT_TYPE_TUPLE));
   g_return_if_fail (timeout_msec == -1 || timeout_msec >= 0);
 
+  reply_type = NULL;
+  split_interface_name = NULL;
+
   simple = g_simple_async_result_new (G_OBJECT (proxy),
                                       callback,
                                       user_data,
@@ -2262,20 +2291,31 @@ g_dbus_proxy_call (GDBusProxy          *proxy,
   target_method_name = was_split ? split_method_name : method_name;
   target_interface_name = was_split ? split_interface_name : proxy->priv->interface_name;
 
-  g_object_set_data_full (G_OBJECT (simple), "-gdbus-proxy-method-name", g_strdup (target_method_name), g_free);
-
   /* Warn if method is unexpected (cf. :g-interface-info) */
-  expected_method_info = NULL;
   if (!was_split)
-    expected_method_info = lookup_method_info_or_warn (proxy, target_method_name);
+    {
+      const GDBusMethodInfo *expected_method_info;
+      expected_method_info = lookup_method_info_or_warn (proxy, target_method_name);
+      if (expected_method_info != NULL)
+        reply_type = _g_dbus_compute_complete_signature (expected_method_info->out_args);
+    }
 
-  if (expected_method_info)
-    reply_type = _g_dbus_compute_complete_signature (expected_method_info->out_args);
-  else
-    reply_type = NULL;
+  destination = NULL;
+  if (proxy->priv->name != NULL)
+    {
+      destination = get_destination_for_call (proxy);
+      if (destination == NULL)
+        {
+          g_simple_async_result_set_error (simple,
+                                           G_IO_ERROR,
+                                           G_IO_ERROR_FAILED,
+                                           _("Cannot invoke method; proxy is for a well-known name without an owner and proxy was constructed with the G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START flag"));
+          goto out;
+        }
+    }
 
   g_dbus_connection_call (proxy->priv->connection,
-                          proxy->priv->name_owner,
+                          destination,
                           proxy->priv->object_path,
                           target_interface_name,
                           target_method_name,
@@ -2287,6 +2327,7 @@ g_dbus_proxy_call (GDBusProxy          *proxy,
                           (GAsyncReadyCallback) reply_cb,
                           simple);
 
+ out:
   if (reply_type != NULL)
     g_variant_type_free (reply_type);
 
@@ -2313,7 +2354,6 @@ g_dbus_proxy_call_finish (GDBusProxy    *proxy,
 {
   GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
   GVariant *value;
-  const char *method_name;
 
   g_return_val_if_fail (G_IS_DBUS_PROXY (proxy), NULL);
   g_return_val_if_fail (G_IS_ASYNC_RESULT (res), NULL);
@@ -2326,8 +2366,7 @@ g_dbus_proxy_call_finish (GDBusProxy    *proxy,
   if (g_simple_async_result_propagate_error (simple, error))
     goto out;
 
-  value = g_simple_async_result_get_op_res_gpointer (simple);
-  method_name = g_object_get_data (G_OBJECT (simple), "-gdbus-proxy-method-name");
+  value = g_variant_ref (g_simple_async_result_get_op_res_gpointer (simple));
 
  out:
   return value;
@@ -2339,7 +2378,8 @@ g_dbus_proxy_call_finish (GDBusProxy    *proxy,
  * @method_name: Name of method to invoke.
  * @parameters: A #GVariant tuple with parameters for the signal or %NULL if not passing parameters.
  * @flags: Flags from the #GDBusCallFlags enumeration.
- * @timeout_msec: The timeout in milliseconds or -1 to use the proxy default timeout.
+ * @timeout_msec: The timeout in milliseconds (with %G_MAXINT meaning
+ *                "infinite") or -1 to use the proxy default timeout.
  * @cancellable: A #GCancellable or %NULL.
  * @error: Return location for error or %NULL.
  *
@@ -2392,9 +2432,9 @@ g_dbus_proxy_call_sync (GDBusProxy      *proxy,
   gboolean was_split;
   gchar *split_interface_name;
   const gchar *split_method_name;
-  const GDBusMethodInfo *expected_method_info;
   const gchar *target_method_name;
   const gchar *target_interface_name;
+  const gchar *destination;
   GVariantType *reply_type;
 
   g_return_val_if_fail (G_IS_DBUS_PROXY (proxy), NULL);
@@ -2403,32 +2443,38 @@ g_dbus_proxy_call_sync (GDBusProxy      *proxy,
   g_return_val_if_fail (timeout_msec == -1 || timeout_msec >= 0, NULL);
   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
 
+  reply_type = NULL;
+
   was_split = maybe_split_method_name (method_name, &split_interface_name, &split_method_name);
   target_method_name = was_split ? split_method_name : method_name;
   target_interface_name = was_split ? split_interface_name : proxy->priv->interface_name;
 
-  if (proxy->priv->expected_interface)
+  /* Warn if method is unexpected (cf. :g-interface-info) */
+  if (!was_split)
     {
-      expected_method_info = g_dbus_interface_info_lookup_method (proxy->priv->expected_interface, target_method_name);
-      if (expected_method_info == NULL)
-        {
-          g_warning ("Trying to invoke method `%s' which isn't in expected interface `%s'",
-                     target_method_name,
-                     target_interface_name);
-        }
+      const GDBusMethodInfo *expected_method_info;
+      expected_method_info = lookup_method_info_or_warn (proxy, target_method_name);
+      if (expected_method_info != NULL)
+        reply_type = _g_dbus_compute_complete_signature (expected_method_info->out_args);
     }
-  else
+
+  destination = NULL;
+  if (proxy->priv->name != NULL)
     {
-      expected_method_info = NULL;
+      destination = get_destination_for_call (proxy);
+      if (destination == NULL)
+        {
+          g_set_error_literal (error,
+                               G_IO_ERROR,
+                               G_IO_ERROR_FAILED,
+                               _("Cannot invoke method; proxy is for a well-known name without an owner and proxy was constructed with the G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START flag"));
+          ret = NULL;
+          goto out;
+        }
     }
 
-  if (expected_method_info)
-    reply_type = _g_dbus_compute_complete_signature (expected_method_info->out_args);
-  else
-    reply_type = NULL;
-
   ret = g_dbus_connection_call_sync (proxy->priv->connection,
-                                     proxy->priv->name_owner,
+                                     destination,
                                      proxy->priv->object_path,
                                      target_interface_name,
                                      target_method_name,
@@ -2439,6 +2485,7 @@ g_dbus_proxy_call_sync (GDBusProxy      *proxy,
                                      cancellable,
                                      error);
 
+ out:
   if (reply_type != NULL)
     g_variant_type_free (reply_type);