Blame gio/gdbuserror.c

Packit ae235b
/* GDBus - GLib D-Bus Library
Packit ae235b
 *
Packit ae235b
 * Copyright (C) 2008-2010 Red Hat, Inc.
Packit ae235b
 *
Packit ae235b
 * This library is free software; you can redistribute it and/or
Packit ae235b
 * modify it under the terms of the GNU Lesser General Public
Packit ae235b
 * License as published by the Free Software Foundation; either
Packit ae235b
 * version 2.1 of the License, or (at your option) any later version.
Packit ae235b
 *
Packit ae235b
 * This library is distributed in the hope that it will be useful,
Packit ae235b
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit ae235b
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit ae235b
 * Lesser General Public License for more details.
Packit ae235b
 *
Packit ae235b
 * You should have received a copy of the GNU Lesser General
Packit ae235b
 * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
Packit ae235b
 *
Packit ae235b
 * Author: David Zeuthen <davidz@redhat.com>
Packit ae235b
 */
Packit ae235b
Packit ae235b
#include "config.h"
Packit ae235b
Packit ae235b
#include <stdlib.h>
Packit ae235b
#include <string.h>
Packit ae235b
Packit ae235b
#include "gdbuserror.h"
Packit ae235b
#include "gioenums.h"
Packit ae235b
#include "gioenumtypes.h"
Packit ae235b
#include "gioerror.h"
Packit ae235b
#include "gdbusprivate.h"
Packit ae235b
Packit ae235b
#include "glibintl.h"
Packit ae235b
Packit ae235b
/**
Packit ae235b
 * SECTION:gdbuserror
Packit ae235b
 * @title: GDBusError
Packit ae235b
 * @short_description: Mapping D-Bus errors to and from GError
Packit ae235b
 * @include: gio/gio.h
Packit ae235b
 *
Packit ae235b
 * All facilities that return errors from remote methods (such as
Packit ae235b
 * g_dbus_connection_call_sync()) use #GError to represent both D-Bus
Packit ae235b
 * errors (e.g. errors returned from the other peer) and locally
Packit ae235b
 * in-process generated errors.
Packit ae235b
 *
Packit ae235b
 * To check if a returned #GError is an error from a remote peer, use
Packit ae235b
 * g_dbus_error_is_remote_error(). To get the actual D-Bus error name,
Packit ae235b
 * use g_dbus_error_get_remote_error(). Before presenting an error,
Packit ae235b
 * always use g_dbus_error_strip_remote_error().
Packit ae235b
 *
Packit ae235b
 * In addition, facilities used to return errors to a remote peer also
Packit ae235b
 * use #GError. See g_dbus_method_invocation_return_error() for
Packit ae235b
 * discussion about how the D-Bus error name is set.
Packit ae235b
 *
Packit ae235b
 * Applications can associate a #GError error domain with a set of D-Bus errors in order to
Packit ae235b
 * automatically map from D-Bus errors to #GError and back. This
Packit ae235b
 * is typically done in the function returning the #GQuark for the
Packit ae235b
 * error domain:
Packit ae235b
 * |[
Packit ae235b
 * // foo-bar-error.h:
Packit ae235b
 *
Packit ae235b
 * #define FOO_BAR_ERROR (foo_bar_error_quark ())
Packit ae235b
 * GQuark foo_bar_error_quark (void);
Packit ae235b
 *
Packit ae235b
 * typedef enum
Packit ae235b
 * {
Packit ae235b
 *   FOO_BAR_ERROR_FAILED,
Packit ae235b
 *   FOO_BAR_ERROR_ANOTHER_ERROR,
Packit ae235b
 *   FOO_BAR_ERROR_SOME_THIRD_ERROR,
Packit ae235b
 *   FOO_BAR_N_ERRORS / *< skip >* /
Packit ae235b
 * } FooBarError;
Packit ae235b
 *
Packit ae235b
 * // foo-bar-error.c:
Packit ae235b
 *
Packit ae235b
 * static const GDBusErrorEntry foo_bar_error_entries[] =
Packit ae235b
 * {
Packit ae235b
 *   {FOO_BAR_ERROR_FAILED,           "org.project.Foo.Bar.Error.Failed"},
Packit ae235b
 *   {FOO_BAR_ERROR_ANOTHER_ERROR,    "org.project.Foo.Bar.Error.AnotherError"},
Packit ae235b
 *   {FOO_BAR_ERROR_SOME_THIRD_ERROR, "org.project.Foo.Bar.Error.SomeThirdError"},
Packit ae235b
 * };
Packit ae235b
 *
Packit ae235b
 * // Ensure that every error code has an associated D-Bus error name
Packit ae235b
 * G_STATIC_ASSERT (G_N_ELEMENTS (foo_bar_error_entries) == FOO_BAR_N_ERRORS);
Packit ae235b
 *
Packit ae235b
 * GQuark
Packit ae235b
 * foo_bar_error_quark (void)
Packit ae235b
 * {
Packit ae235b
 *   static volatile gsize quark_volatile = 0;
Packit ae235b
 *   g_dbus_error_register_error_domain ("foo-bar-error-quark",
Packit ae235b
 *                                       &quark_volatile,
Packit ae235b
 *                                       foo_bar_error_entries,
Packit ae235b
 *                                       G_N_ELEMENTS (foo_bar_error_entries));
Packit ae235b
 *   return (GQuark) quark_volatile;
Packit ae235b
 * }
Packit ae235b
 * ]|
Packit ae235b
 * With this setup, a D-Bus peer can transparently pass e.g. %FOO_BAR_ERROR_ANOTHER_ERROR and
Packit ae235b
 * other peers will see the D-Bus error name org.project.Foo.Bar.Error.AnotherError.
Packit ae235b
 *
Packit ae235b
 * If the other peer is using GDBus, and has registered the association with
Packit ae235b
 * g_dbus_error_register_error_domain() in advance (e.g. by invoking the %FOO_BAR_ERROR quark
Packit ae235b
 * generation itself in the previous example) the peer will see also %FOO_BAR_ERROR_ANOTHER_ERROR instead
Packit ae235b
 * of %G_IO_ERROR_DBUS_ERROR. Note that GDBus clients can still recover
Packit ae235b
 * org.project.Foo.Bar.Error.AnotherError using g_dbus_error_get_remote_error().
Packit ae235b
 *
Packit ae235b
 * Note that the %G_DBUS_ERROR error domain is intended only
Packit ae235b
 * for returning errors from a remote message bus process. Errors
Packit ae235b
 * generated locally in-process by e.g. #GDBusConnection should use the
Packit ae235b
 * %G_IO_ERROR domain.
Packit ae235b
 */
Packit ae235b
Packit ae235b
static const GDBusErrorEntry g_dbus_error_entries[] =
Packit ae235b
{
Packit ae235b
  {G_DBUS_ERROR_FAILED,                           "org.freedesktop.DBus.Error.Failed"},
Packit ae235b
  {G_DBUS_ERROR_NO_MEMORY,                        "org.freedesktop.DBus.Error.NoMemory"},
Packit ae235b
  {G_DBUS_ERROR_SERVICE_UNKNOWN,                  "org.freedesktop.DBus.Error.ServiceUnknown"},
Packit ae235b
  {G_DBUS_ERROR_NAME_HAS_NO_OWNER,                "org.freedesktop.DBus.Error.NameHasNoOwner"},
Packit ae235b
  {G_DBUS_ERROR_NO_REPLY,                         "org.freedesktop.DBus.Error.NoReply"},
Packit ae235b
  {G_DBUS_ERROR_IO_ERROR,                         "org.freedesktop.DBus.Error.IOError"},
Packit ae235b
  {G_DBUS_ERROR_BAD_ADDRESS,                      "org.freedesktop.DBus.Error.BadAddress"},
Packit ae235b
  {G_DBUS_ERROR_NOT_SUPPORTED,                    "org.freedesktop.DBus.Error.NotSupported"},
Packit ae235b
  {G_DBUS_ERROR_LIMITS_EXCEEDED,                  "org.freedesktop.DBus.Error.LimitsExceeded"},
Packit ae235b
  {G_DBUS_ERROR_ACCESS_DENIED,                    "org.freedesktop.DBus.Error.AccessDenied"},
Packit ae235b
  {G_DBUS_ERROR_AUTH_FAILED,                      "org.freedesktop.DBus.Error.AuthFailed"},
Packit ae235b
  {G_DBUS_ERROR_NO_SERVER,                        "org.freedesktop.DBus.Error.NoServer"},
Packit ae235b
  {G_DBUS_ERROR_TIMEOUT,                          "org.freedesktop.DBus.Error.Timeout"},
Packit ae235b
  {G_DBUS_ERROR_NO_NETWORK,                       "org.freedesktop.DBus.Error.NoNetwork"},
Packit ae235b
  {G_DBUS_ERROR_ADDRESS_IN_USE,                   "org.freedesktop.DBus.Error.AddressInUse"},
Packit ae235b
  {G_DBUS_ERROR_DISCONNECTED,                     "org.freedesktop.DBus.Error.Disconnected"},
Packit ae235b
  {G_DBUS_ERROR_INVALID_ARGS,                     "org.freedesktop.DBus.Error.InvalidArgs"},
Packit ae235b
  {G_DBUS_ERROR_FILE_NOT_FOUND,                   "org.freedesktop.DBus.Error.FileNotFound"},
Packit ae235b
  {G_DBUS_ERROR_FILE_EXISTS,                      "org.freedesktop.DBus.Error.FileExists"},
Packit ae235b
  {G_DBUS_ERROR_UNKNOWN_METHOD,                   "org.freedesktop.DBus.Error.UnknownMethod"},
Packit ae235b
  {G_DBUS_ERROR_TIMED_OUT,                        "org.freedesktop.DBus.Error.TimedOut"},
Packit ae235b
  {G_DBUS_ERROR_MATCH_RULE_NOT_FOUND,             "org.freedesktop.DBus.Error.MatchRuleNotFound"},
Packit ae235b
  {G_DBUS_ERROR_MATCH_RULE_INVALID,               "org.freedesktop.DBus.Error.MatchRuleInvalid"},
Packit ae235b
  {G_DBUS_ERROR_SPAWN_EXEC_FAILED,                "org.freedesktop.DBus.Error.Spawn.ExecFailed"},
Packit ae235b
  {G_DBUS_ERROR_SPAWN_FORK_FAILED,                "org.freedesktop.DBus.Error.Spawn.ForkFailed"},
Packit ae235b
  {G_DBUS_ERROR_SPAWN_CHILD_EXITED,               "org.freedesktop.DBus.Error.Spawn.ChildExited"},
Packit ae235b
  {G_DBUS_ERROR_SPAWN_CHILD_SIGNALED,             "org.freedesktop.DBus.Error.Spawn.ChildSignaled"},
Packit ae235b
  {G_DBUS_ERROR_SPAWN_FAILED,                     "org.freedesktop.DBus.Error.Spawn.Failed"},
Packit ae235b
  {G_DBUS_ERROR_SPAWN_SETUP_FAILED,               "org.freedesktop.DBus.Error.Spawn.FailedToSetup"},
Packit ae235b
  {G_DBUS_ERROR_SPAWN_CONFIG_INVALID,             "org.freedesktop.DBus.Error.Spawn.ConfigInvalid"},
Packit ae235b
  {G_DBUS_ERROR_SPAWN_SERVICE_INVALID,            "org.freedesktop.DBus.Error.Spawn.ServiceNotValid"},
Packit ae235b
  {G_DBUS_ERROR_SPAWN_SERVICE_NOT_FOUND,          "org.freedesktop.DBus.Error.Spawn.ServiceNotFound"},
Packit ae235b
  {G_DBUS_ERROR_SPAWN_PERMISSIONS_INVALID,        "org.freedesktop.DBus.Error.Spawn.PermissionsInvalid"},
Packit ae235b
  {G_DBUS_ERROR_SPAWN_FILE_INVALID,               "org.freedesktop.DBus.Error.Spawn.FileInvalid"},
Packit ae235b
  {G_DBUS_ERROR_SPAWN_NO_MEMORY,                  "org.freedesktop.DBus.Error.Spawn.NoMemory"},
Packit ae235b
  {G_DBUS_ERROR_UNIX_PROCESS_ID_UNKNOWN,          "org.freedesktop.DBus.Error.UnixProcessIdUnknown"},
Packit ae235b
  {G_DBUS_ERROR_INVALID_SIGNATURE,                "org.freedesktop.DBus.Error.InvalidSignature"},
Packit ae235b
  {G_DBUS_ERROR_INVALID_FILE_CONTENT,             "org.freedesktop.DBus.Error.InvalidFileContent"},
Packit ae235b
  {G_DBUS_ERROR_SELINUX_SECURITY_CONTEXT_UNKNOWN, "org.freedesktop.DBus.Error.SELinuxSecurityContextUnknown"},
Packit ae235b
  {G_DBUS_ERROR_ADT_AUDIT_DATA_UNKNOWN,           "org.freedesktop.DBus.Error.AdtAuditDataUnknown"},
Packit ae235b
  {G_DBUS_ERROR_OBJECT_PATH_IN_USE,               "org.freedesktop.DBus.Error.ObjectPathInUse"},
Packit ae235b
  {G_DBUS_ERROR_UNKNOWN_OBJECT,                   "org.freedesktop.DBus.Error.UnknownObject"},
Packit ae235b
  {G_DBUS_ERROR_UNKNOWN_INTERFACE,                "org.freedesktop.DBus.Error.UnknownInterface"},
Packit ae235b
  {G_DBUS_ERROR_UNKNOWN_PROPERTY,                 "org.freedesktop.DBus.Error.UnknownProperty"},
Packit ae235b
  {G_DBUS_ERROR_PROPERTY_READ_ONLY,               "org.freedesktop.DBus.Error.PropertyReadOnly"},
Packit ae235b
};
Packit ae235b
Packit ae235b
GQuark
Packit ae235b
g_dbus_error_quark (void)
Packit ae235b
{
Packit ae235b
  G_STATIC_ASSERT (G_N_ELEMENTS (g_dbus_error_entries) - 1 == G_DBUS_ERROR_PROPERTY_READ_ONLY);
Packit ae235b
  static volatile gsize quark_volatile = 0;
Packit ae235b
  g_dbus_error_register_error_domain ("g-dbus-error-quark",
Packit ae235b
                                      &quark_volatile,
Packit ae235b
                                      g_dbus_error_entries,
Packit ae235b
                                      G_N_ELEMENTS (g_dbus_error_entries));
Packit ae235b
  return (GQuark) quark_volatile;
Packit ae235b
}
Packit ae235b
Packit ae235b
/**
Packit ae235b
 * g_dbus_error_register_error_domain:
Packit ae235b
 * @error_domain_quark_name: The error domain name.
Packit ae235b
 * @quark_volatile: A pointer where to store the #GQuark.
Packit ae235b
 * @entries: (array length=num_entries): A pointer to @num_entries #GDBusErrorEntry struct items.
Packit ae235b
 * @num_entries: Number of items to register.
Packit ae235b
 *
Packit ae235b
 * Helper function for associating a #GError error domain with D-Bus error names.
Packit ae235b
 *
Packit ae235b
 * Since: 2.26
Packit ae235b
 */
Packit ae235b
void
Packit ae235b
g_dbus_error_register_error_domain (const gchar           *error_domain_quark_name,
Packit ae235b
                                    volatile gsize        *quark_volatile,
Packit ae235b
                                    const GDBusErrorEntry *entries,
Packit ae235b
                                    guint                  num_entries)
Packit ae235b
{
Packit ae235b
  g_return_if_fail (error_domain_quark_name != NULL);
Packit ae235b
  g_return_if_fail (quark_volatile != NULL);
Packit ae235b
  g_return_if_fail (entries != NULL);
Packit ae235b
  g_return_if_fail (num_entries > 0);
Packit ae235b
Packit ae235b
  if (g_once_init_enter (quark_volatile))
Packit ae235b
    {
Packit ae235b
      guint n;
Packit ae235b
      GQuark quark;
Packit ae235b
Packit ae235b
      quark = g_quark_from_static_string (error_domain_quark_name);
Packit ae235b
Packit ae235b
      for (n = 0; n < num_entries; n++)
Packit ae235b
        {
Packit ae235b
          g_warn_if_fail (g_dbus_error_register_error (quark,
Packit ae235b
                                                       entries[n].error_code,
Packit ae235b
                                                       entries[n].dbus_error_name));
Packit ae235b
        }
Packit ae235b
      g_once_init_leave (quark_volatile, quark);
Packit ae235b
    }
Packit ae235b
}
Packit ae235b
Packit ae235b
static gboolean
Packit ae235b
_g_dbus_error_decode_gerror (const gchar *dbus_name,
Packit ae235b
                             GQuark      *out_error_domain,
Packit ae235b
                             gint        *out_error_code)
Packit ae235b
{
Packit ae235b
  gboolean ret;
Packit ae235b
  guint n;
Packit ae235b
  GString *s;
Packit ae235b
  gchar *domain_quark_string;
Packit ae235b
Packit ae235b
  ret = FALSE;
Packit ae235b
  s = NULL;
Packit ae235b
Packit ae235b
  if (g_str_has_prefix (dbus_name, "org.gtk.GDBus.UnmappedGError.Quark._"))
Packit ae235b
    {
Packit ae235b
      s = g_string_new (NULL);
Packit ae235b
Packit ae235b
      for (n = sizeof "org.gtk.GDBus.UnmappedGError.Quark._" - 1;
Packit ae235b
           dbus_name[n] != '.' && dbus_name[n] != '\0';
Packit ae235b
           n++)
Packit ae235b
        {
Packit ae235b
          if (g_ascii_isalnum (dbus_name[n]))
Packit ae235b
            {
Packit ae235b
              g_string_append_c (s, dbus_name[n]);
Packit ae235b
            }
Packit ae235b
          else if (dbus_name[n] == '_')
Packit ae235b
            {
Packit ae235b
              guint nibble_top;
Packit ae235b
              guint nibble_bottom;
Packit ae235b
Packit ae235b
              n++;
Packit ae235b
Packit ae235b
              nibble_top = dbus_name[n];
Packit ae235b
              if (nibble_top >= '0' && nibble_top <= '9')
Packit ae235b
                nibble_top -= '0';
Packit ae235b
              else if (nibble_top >= 'a' && nibble_top <= 'f')
Packit ae235b
                nibble_top -= ('a' - 10);
Packit ae235b
              else
Packit ae235b
                goto not_mapped;
Packit ae235b
Packit ae235b
              n++;
Packit ae235b
Packit ae235b
              nibble_bottom = dbus_name[n];
Packit ae235b
              if (nibble_bottom >= '0' && nibble_bottom <= '9')
Packit ae235b
                nibble_bottom -= '0';
Packit ae235b
              else if (nibble_bottom >= 'a' && nibble_bottom <= 'f')
Packit ae235b
                nibble_bottom -= ('a' - 10);
Packit ae235b
              else
Packit ae235b
                goto not_mapped;
Packit ae235b
Packit ae235b
              g_string_append_c (s, (nibble_top<<4) | nibble_bottom);
Packit ae235b
            }
Packit ae235b
          else
Packit ae235b
            {
Packit ae235b
              goto not_mapped;
Packit ae235b
            }
Packit ae235b
        }
Packit ae235b
Packit ae235b
      if (!g_str_has_prefix (dbus_name + n, ".Code"))
Packit ae235b
        goto not_mapped;
Packit ae235b
Packit ae235b
      domain_quark_string = g_string_free (s, FALSE);
Packit ae235b
      s = NULL;
Packit ae235b
Packit ae235b
      if (out_error_domain != NULL)
Packit ae235b
        *out_error_domain = g_quark_from_string (domain_quark_string);
Packit ae235b
      g_free (domain_quark_string);
Packit ae235b
Packit ae235b
      if (out_error_code != NULL)
Packit ae235b
        *out_error_code = atoi (dbus_name + n + sizeof ".Code" - 1);
Packit ae235b
Packit ae235b
      ret = TRUE;
Packit ae235b
    }
Packit ae235b
Packit ae235b
 not_mapped:
Packit ae235b
Packit ae235b
  if (s != NULL)
Packit ae235b
    g_string_free (s, TRUE);
Packit ae235b
Packit ae235b
  return ret;
Packit ae235b
}
Packit ae235b
Packit ae235b
/* ---------------------------------------------------------------------------------------------------- */
Packit ae235b
Packit ae235b
typedef struct
Packit ae235b
{
Packit ae235b
  GQuark error_domain;
Packit ae235b
  gint   error_code;
Packit ae235b
} QuarkCodePair;
Packit ae235b
Packit ae235b
static guint
Packit ae235b
quark_code_pair_hash_func (const QuarkCodePair *pair)
Packit ae235b
{
Packit ae235b
  gint val;
Packit ae235b
  val = pair->error_domain + pair->error_code;
Packit ae235b
  return g_int_hash (&val;;
Packit ae235b
}
Packit ae235b
Packit ae235b
static gboolean
Packit ae235b
quark_code_pair_equal_func (const QuarkCodePair *a,
Packit ae235b
                            const QuarkCodePair *b)
Packit ae235b
{
Packit ae235b
  return (a->error_domain == b->error_domain) && (a->error_code == b->error_code);
Packit ae235b
}
Packit ae235b
Packit ae235b
typedef struct
Packit ae235b
{
Packit ae235b
  QuarkCodePair pair;
Packit ae235b
  gchar *dbus_error_name;
Packit ae235b
} RegisteredError;
Packit ae235b
Packit ae235b
static void
Packit ae235b
registered_error_free (RegisteredError *re)
Packit ae235b
{
Packit ae235b
  g_free (re->dbus_error_name);
Packit ae235b
  g_free (re);
Packit ae235b
}
Packit ae235b
Packit ae235b
G_LOCK_DEFINE_STATIC (error_lock);
Packit ae235b
Packit ae235b
/* maps from QuarkCodePair* -> RegisteredError* */
Packit ae235b
static GHashTable *quark_code_pair_to_re = NULL;
Packit ae235b
Packit ae235b
/* maps from gchar* -> RegisteredError* */
Packit ae235b
static GHashTable *dbus_error_name_to_re = NULL;
Packit ae235b
Packit ae235b
/**
Packit ae235b
 * g_dbus_error_register_error:
Packit ae235b
 * @error_domain: A #GQuark for a error domain.
Packit ae235b
 * @error_code: An error code.
Packit ae235b
 * @dbus_error_name: A D-Bus error name.
Packit ae235b
 *
Packit ae235b
 * Creates an association to map between @dbus_error_name and
Packit ae235b
 * #GErrors specified by @error_domain and @error_code.
Packit ae235b
 *
Packit ae235b
 * This is typically done in the routine that returns the #GQuark for
Packit ae235b
 * an error domain.
Packit ae235b
 *
Packit ae235b
 * Returns: %TRUE if the association was created, %FALSE if it already
Packit ae235b
 * exists.
Packit ae235b
 *
Packit ae235b
 * Since: 2.26
Packit ae235b
 */
Packit ae235b
gboolean
Packit ae235b
g_dbus_error_register_error (GQuark       error_domain,
Packit ae235b
                             gint         error_code,
Packit ae235b
                             const gchar *dbus_error_name)
Packit ae235b
{
Packit ae235b
  gboolean ret;
Packit ae235b
  QuarkCodePair pair;
Packit ae235b
  RegisteredError *re;
Packit ae235b
Packit ae235b
  g_return_val_if_fail (dbus_error_name != NULL, FALSE);
Packit ae235b
Packit ae235b
  ret = FALSE;
Packit ae235b
Packit ae235b
  G_LOCK (error_lock);
Packit ae235b
Packit ae235b
  if (quark_code_pair_to_re == NULL)
Packit ae235b
    {
Packit ae235b
      g_assert (dbus_error_name_to_re == NULL); /* check invariant */
Packit ae235b
      quark_code_pair_to_re = g_hash_table_new ((GHashFunc) quark_code_pair_hash_func,
Packit ae235b
                                                (GEqualFunc) quark_code_pair_equal_func);
Packit ae235b
      dbus_error_name_to_re = g_hash_table_new_full (g_str_hash,
Packit ae235b
                                                     g_str_equal,
Packit ae235b
                                                     NULL,
Packit ae235b
                                                     (GDestroyNotify) registered_error_free);
Packit ae235b
    }
Packit ae235b
Packit ae235b
  if (g_hash_table_lookup (dbus_error_name_to_re, dbus_error_name) != NULL)
Packit ae235b
    goto out;
Packit ae235b
Packit ae235b
  pair.error_domain = error_domain;
Packit ae235b
  pair.error_code = error_code;
Packit ae235b
Packit ae235b
  if (g_hash_table_lookup (quark_code_pair_to_re, &pair) != NULL)
Packit ae235b
    goto out;
Packit ae235b
Packit ae235b
  re = g_new0 (RegisteredError, 1);
Packit ae235b
  re->pair = pair;
Packit ae235b
  re->dbus_error_name = g_strdup (dbus_error_name);
Packit ae235b
Packit ae235b
  g_hash_table_insert (quark_code_pair_to_re, &(re->pair), re);
Packit ae235b
  g_hash_table_insert (dbus_error_name_to_re, re->dbus_error_name, re);
Packit ae235b
Packit ae235b
  ret = TRUE;
Packit ae235b
Packit ae235b
 out:
Packit ae235b
  G_UNLOCK (error_lock);
Packit ae235b
  return ret;
Packit ae235b
}
Packit ae235b
Packit ae235b
/**
Packit ae235b
 * g_dbus_error_unregister_error:
Packit ae235b
 * @error_domain: A #GQuark for a error domain.
Packit ae235b
 * @error_code: An error code.
Packit ae235b
 * @dbus_error_name: A D-Bus error name.
Packit ae235b
 *
Packit ae235b
 * Destroys an association previously set up with g_dbus_error_register_error().
Packit ae235b
 *
Packit ae235b
 * Returns: %TRUE if the association was destroyed, %FALSE if it wasn't found.
Packit ae235b
 *
Packit ae235b
 * Since: 2.26
Packit ae235b
 */
Packit ae235b
gboolean
Packit ae235b
g_dbus_error_unregister_error (GQuark       error_domain,
Packit ae235b
                               gint         error_code,
Packit ae235b
                               const gchar *dbus_error_name)
Packit ae235b
{
Packit ae235b
  gboolean ret;
Packit ae235b
  RegisteredError *re;
Packit ae235b
  guint hash_size;
Packit ae235b
Packit ae235b
  g_return_val_if_fail (dbus_error_name != NULL, FALSE);
Packit ae235b
Packit ae235b
  ret = FALSE;
Packit ae235b
Packit ae235b
  G_LOCK (error_lock);
Packit ae235b
Packit ae235b
  if (dbus_error_name_to_re == NULL)
Packit ae235b
    {
Packit ae235b
      g_assert (quark_code_pair_to_re == NULL); /* check invariant */
Packit ae235b
      goto out;
Packit ae235b
    }
Packit ae235b
Packit ae235b
  re = g_hash_table_lookup (dbus_error_name_to_re, dbus_error_name);
Packit ae235b
  if (re == NULL)
Packit ae235b
    {
Packit ae235b
      QuarkCodePair pair;
Packit ae235b
      pair.error_domain = error_domain;
Packit ae235b
      pair.error_code = error_code;
Packit ae235b
      g_warn_if_fail (g_hash_table_lookup (quark_code_pair_to_re, &pair) == NULL); /* check invariant */
Packit ae235b
      goto out;
Packit ae235b
    }
Packit ae235b
Packit ae235b
  ret = TRUE;
Packit ae235b
Packit ae235b
  g_warn_if_fail (g_hash_table_lookup (quark_code_pair_to_re, &(re->pair)) == re); /* check invariant */
Packit ae235b
Packit ae235b
  g_warn_if_fail (g_hash_table_remove (quark_code_pair_to_re, &(re->pair)));
Packit ae235b
  g_warn_if_fail (g_hash_table_remove (dbus_error_name_to_re, re->dbus_error_name));
Packit ae235b
Packit ae235b
  /* destroy hashes if empty */
Packit ae235b
  hash_size = g_hash_table_size (dbus_error_name_to_re);
Packit ae235b
  if (hash_size == 0)
Packit ae235b
    {
Packit ae235b
      g_warn_if_fail (g_hash_table_size (quark_code_pair_to_re) == 0); /* check invariant */
Packit ae235b
Packit ae235b
      g_hash_table_unref (dbus_error_name_to_re);
Packit ae235b
      dbus_error_name_to_re = NULL;
Packit ae235b
      g_hash_table_unref (quark_code_pair_to_re);
Packit ae235b
      quark_code_pair_to_re = NULL;
Packit ae235b
    }
Packit ae235b
  else
Packit ae235b
    {
Packit ae235b
      g_warn_if_fail (g_hash_table_size (quark_code_pair_to_re) == hash_size); /* check invariant */
Packit ae235b
    }
Packit ae235b
Packit ae235b
 out:
Packit ae235b
  G_UNLOCK (error_lock);
Packit ae235b
  return ret;
Packit ae235b
}
Packit ae235b
Packit ae235b
/* ---------------------------------------------------------------------------------------------------- */
Packit ae235b
Packit ae235b
/**
Packit ae235b
 * g_dbus_error_is_remote_error:
Packit ae235b
 * @error: A #GError.
Packit ae235b
 *
Packit ae235b
 * Checks if @error represents an error received via D-Bus from a remote peer. If so,
Packit ae235b
 * use g_dbus_error_get_remote_error() to get the name of the error.
Packit ae235b
 *
Packit ae235b
 * Returns: %TRUE if @error represents an error from a remote peer,
Packit ae235b
 * %FALSE otherwise.
Packit ae235b
 *
Packit ae235b
 * Since: 2.26
Packit ae235b
 */
Packit ae235b
gboolean
Packit ae235b
g_dbus_error_is_remote_error (const GError *error)
Packit ae235b
{
Packit ae235b
  g_return_val_if_fail (error != NULL, FALSE);
Packit ae235b
  return g_str_has_prefix (error->message, "GDBus.Error:");
Packit ae235b
}
Packit ae235b
Packit ae235b
Packit ae235b
/**
Packit ae235b
 * g_dbus_error_get_remote_error:
Packit ae235b
 * @error: a #GError
Packit ae235b
 *
Packit ae235b
 * Gets the D-Bus error name used for @error, if any.
Packit ae235b
 *
Packit ae235b
 * This function is guaranteed to return a D-Bus error name for all
Packit ae235b
 * #GErrors returned from functions handling remote method calls
Packit ae235b
 * (e.g. g_dbus_connection_call_finish()) unless
Packit ae235b
 * g_dbus_error_strip_remote_error() has been used on @error.
Packit ae235b
 *
Packit ae235b
 * Returns: an allocated string or %NULL if the D-Bus error name
Packit ae235b
 *     could not be found. Free with g_free().
Packit ae235b
 *
Packit ae235b
 * Since: 2.26
Packit ae235b
 */
Packit ae235b
gchar *
Packit ae235b
g_dbus_error_get_remote_error (const GError *error)
Packit ae235b
{
Packit ae235b
  RegisteredError *re;
Packit ae235b
  gchar *ret;
Packit ae235b
Packit ae235b
  g_return_val_if_fail (error != NULL, NULL);
Packit ae235b
Packit ae235b
  /* Ensure that e.g. G_DBUS_ERROR is registered using g_dbus_error_register_error() */
Packit ae235b
  _g_dbus_initialize ();
Packit ae235b
Packit ae235b
  ret = NULL;
Packit ae235b
Packit ae235b
  G_LOCK (error_lock);
Packit ae235b
Packit ae235b
  re = NULL;
Packit ae235b
  if (quark_code_pair_to_re != NULL)
Packit ae235b
    {
Packit ae235b
      QuarkCodePair pair;
Packit ae235b
      pair.error_domain = error->domain;
Packit ae235b
      pair.error_code = error->code;
Packit ae235b
      g_assert (dbus_error_name_to_re != NULL); /* check invariant */
Packit ae235b
      re = g_hash_table_lookup (quark_code_pair_to_re, &pair);
Packit ae235b
    }
Packit ae235b
Packit ae235b
  if (re != NULL)
Packit ae235b
    {
Packit ae235b
      ret = g_strdup (re->dbus_error_name);
Packit ae235b
    }
Packit ae235b
  else
Packit ae235b
    {
Packit ae235b
      if (g_str_has_prefix (error->message, "GDBus.Error:"))
Packit ae235b
        {
Packit ae235b
          const gchar *begin;
Packit ae235b
          const gchar *end;
Packit ae235b
          begin = error->message + sizeof ("GDBus.Error:") -1;
Packit ae235b
          end = strstr (begin, ":");
Packit ae235b
          if (end != NULL && end[1] == ' ')
Packit ae235b
            {
Packit ae235b
              ret = g_strndup (begin, end - begin);
Packit ae235b
            }
Packit ae235b
        }
Packit ae235b
    }
Packit ae235b
Packit ae235b
  G_UNLOCK (error_lock);
Packit ae235b
Packit ae235b
  return ret;
Packit ae235b
}
Packit ae235b
Packit ae235b
/* ---------------------------------------------------------------------------------------------------- */
Packit ae235b
Packit ae235b
/**
Packit ae235b
 * g_dbus_error_new_for_dbus_error:
Packit ae235b
 * @dbus_error_name: D-Bus error name.
Packit ae235b
 * @dbus_error_message: D-Bus error message.
Packit ae235b
 *
Packit ae235b
 * Creates a #GError based on the contents of @dbus_error_name and
Packit ae235b
 * @dbus_error_message.
Packit ae235b
 *
Packit ae235b
 * Errors registered with g_dbus_error_register_error() will be looked
Packit ae235b
 * up using @dbus_error_name and if a match is found, the error domain
Packit ae235b
 * and code is used. Applications can use g_dbus_error_get_remote_error()
Packit ae235b
 * to recover @dbus_error_name.
Packit ae235b
 *
Packit ae235b
 * If a match against a registered error is not found and the D-Bus
Packit ae235b
 * error name is in a form as returned by g_dbus_error_encode_gerror()
Packit ae235b
 * the error domain and code encoded in the name is used to
Packit ae235b
 * create the #GError. Also, @dbus_error_name is added to the error message
Packit ae235b
 * such that it can be recovered with g_dbus_error_get_remote_error().
Packit ae235b
 *
Packit ae235b
 * Otherwise, a #GError with the error code %G_IO_ERROR_DBUS_ERROR
Packit ae235b
 * in the #G_IO_ERROR error domain is returned. Also, @dbus_error_name is
Packit ae235b
 * added to the error message such that it can be recovered with
Packit ae235b
 * g_dbus_error_get_remote_error().
Packit ae235b
 *
Packit ae235b
 * In all three cases, @dbus_error_name can always be recovered from the
Packit ae235b
 * returned #GError using the g_dbus_error_get_remote_error() function
Packit ae235b
 * (unless g_dbus_error_strip_remote_error() hasn't been used on the returned error).
Packit ae235b
 *
Packit ae235b
 * This function is typically only used in object mappings to prepare
Packit ae235b
 * #GError instances for applications. Regular applications should not use
Packit ae235b
 * it.
Packit ae235b
 *
Packit ae235b
 * Returns: An allocated #GError. Free with g_error_free().
Packit ae235b
 *
Packit ae235b
 * Since: 2.26
Packit ae235b
 */
Packit ae235b
GError *
Packit ae235b
g_dbus_error_new_for_dbus_error (const gchar *dbus_error_name,
Packit ae235b
                                 const gchar *dbus_error_message)
Packit ae235b
{
Packit ae235b
  GError *error;
Packit ae235b
  RegisteredError *re;
Packit ae235b
Packit ae235b
  g_return_val_if_fail (dbus_error_name != NULL, NULL);
Packit ae235b
  g_return_val_if_fail (dbus_error_message != NULL, NULL);
Packit ae235b
Packit ae235b
  /* Ensure that e.g. G_DBUS_ERROR is registered using g_dbus_error_register_error() */
Packit ae235b
  _g_dbus_initialize ();
Packit ae235b
Packit ae235b
  G_LOCK (error_lock);
Packit ae235b
Packit ae235b
  re = NULL;
Packit ae235b
  if (dbus_error_name_to_re != NULL)
Packit ae235b
    {
Packit ae235b
      g_assert (quark_code_pair_to_re != NULL); /* check invariant */
Packit ae235b
      re = g_hash_table_lookup (dbus_error_name_to_re, dbus_error_name);
Packit ae235b
    }
Packit ae235b
Packit ae235b
  if (re != NULL)
Packit ae235b
    {
Packit ae235b
      error = g_error_new (re->pair.error_domain,
Packit ae235b
                           re->pair.error_code,
Packit ae235b
                           "GDBus.Error:%s: %s",
Packit ae235b
                           dbus_error_name,
Packit ae235b
                           dbus_error_message);
Packit ae235b
    }
Packit ae235b
  else
Packit ae235b
    {
Packit ae235b
      GQuark error_domain = 0;
Packit ae235b
      gint error_code = 0;
Packit ae235b
Packit ae235b
      if (_g_dbus_error_decode_gerror (dbus_error_name,
Packit ae235b
                                       &error_domain,
Packit ae235b
                                       &error_code))
Packit ae235b
        {
Packit ae235b
          error = g_error_new (error_domain,
Packit ae235b
                               error_code,
Packit ae235b
                               "GDBus.Error:%s: %s",
Packit ae235b
                               dbus_error_name,
Packit ae235b
                               dbus_error_message);
Packit ae235b
        }
Packit ae235b
      else
Packit ae235b
        {
Packit ae235b
          error = g_error_new (G_IO_ERROR,
Packit ae235b
                               G_IO_ERROR_DBUS_ERROR,
Packit ae235b
                               "GDBus.Error:%s: %s",
Packit ae235b
                               dbus_error_name,
Packit ae235b
                               dbus_error_message);
Packit ae235b
        }
Packit ae235b
    }
Packit ae235b
Packit ae235b
  G_UNLOCK (error_lock);
Packit ae235b
  return error;
Packit ae235b
}
Packit ae235b
Packit ae235b
/**
Packit ae235b
 * g_dbus_error_set_dbus_error:
Packit ae235b
 * @error: A pointer to a #GError or %NULL.
Packit ae235b
 * @dbus_error_name: D-Bus error name.
Packit ae235b
 * @dbus_error_message: D-Bus error message.
Packit ae235b
 * @format: (nullable): printf()-style format to prepend to @dbus_error_message or %NULL.
Packit ae235b
 * @...: Arguments for @format.
Packit ae235b
 *
Packit ae235b
 * Does nothing if @error is %NULL. Otherwise sets *@error to
Packit ae235b
 * a new #GError created with g_dbus_error_new_for_dbus_error()
Packit ae235b
 * with @dbus_error_message prepend with @format (unless %NULL).
Packit ae235b
 *
Packit ae235b
 * Since: 2.26
Packit ae235b
 */
Packit ae235b
void
Packit ae235b
g_dbus_error_set_dbus_error (GError      **error,
Packit ae235b
                             const gchar  *dbus_error_name,
Packit ae235b
                             const gchar  *dbus_error_message,
Packit ae235b
                             const gchar  *format,
Packit ae235b
                             ...)
Packit ae235b
{
Packit ae235b
  g_return_if_fail (error == NULL || *error == NULL);
Packit ae235b
  g_return_if_fail (dbus_error_name != NULL);
Packit ae235b
  g_return_if_fail (dbus_error_message != NULL);
Packit ae235b
Packit ae235b
  if (error == NULL)
Packit ae235b
    return;
Packit ae235b
Packit ae235b
  if (format == NULL)
Packit ae235b
    {
Packit ae235b
      *error = g_dbus_error_new_for_dbus_error (dbus_error_name, dbus_error_message);
Packit ae235b
    }
Packit ae235b
  else
Packit ae235b
    {
Packit ae235b
      va_list var_args;
Packit ae235b
      va_start (var_args, format);
Packit ae235b
      g_dbus_error_set_dbus_error_valist (error,
Packit ae235b
                                          dbus_error_name,
Packit ae235b
                                          dbus_error_message,
Packit ae235b
                                          format,
Packit ae235b
                                          var_args);
Packit ae235b
      va_end (var_args);
Packit ae235b
    }
Packit ae235b
}
Packit ae235b
Packit ae235b
/**
Packit ae235b
 * g_dbus_error_set_dbus_error_valist:
Packit ae235b
 * @error: A pointer to a #GError or %NULL.
Packit ae235b
 * @dbus_error_name: D-Bus error name.
Packit ae235b
 * @dbus_error_message: D-Bus error message.
Packit ae235b
 * @format: (nullable): printf()-style format to prepend to @dbus_error_message or %NULL.
Packit ae235b
 * @var_args: Arguments for @format.
Packit ae235b
 *
Packit ae235b
 * Like g_dbus_error_set_dbus_error() but intended for language bindings.
Packit ae235b
 *
Packit ae235b
 * Since: 2.26
Packit ae235b
 */
Packit ae235b
void
Packit ae235b
g_dbus_error_set_dbus_error_valist (GError      **error,
Packit ae235b
                                    const gchar  *dbus_error_name,
Packit ae235b
                                    const gchar  *dbus_error_message,
Packit ae235b
                                    const gchar  *format,
Packit ae235b
                                    va_list       var_args)
Packit ae235b
{
Packit ae235b
  g_return_if_fail (error == NULL || *error == NULL);
Packit ae235b
  g_return_if_fail (dbus_error_name != NULL);
Packit ae235b
  g_return_if_fail (dbus_error_message != NULL);
Packit ae235b
Packit ae235b
  if (error == NULL)
Packit ae235b
    return;
Packit ae235b
Packit ae235b
  if (format != NULL)
Packit ae235b
    {
Packit ae235b
      gchar *message;
Packit ae235b
      gchar *s;
Packit ae235b
      message = g_strdup_vprintf (format, var_args);
Packit ae235b
      s = g_strdup_printf ("%s: %s", message, dbus_error_message);
Packit ae235b
      *error = g_dbus_error_new_for_dbus_error (dbus_error_name, s);
Packit ae235b
      g_free (s);
Packit ae235b
      g_free (message);
Packit ae235b
    }
Packit ae235b
  else
Packit ae235b
    {
Packit ae235b
      *error = g_dbus_error_new_for_dbus_error (dbus_error_name, dbus_error_message);
Packit ae235b
    }
Packit ae235b
}
Packit ae235b
Packit ae235b
/**
Packit ae235b
 * g_dbus_error_strip_remote_error:
Packit ae235b
 * @error: A #GError.
Packit ae235b
 *
Packit ae235b
 * Looks for extra information in the error message used to recover
Packit ae235b
 * the D-Bus error name and strips it if found. If stripped, the
Packit ae235b
 * message field in @error will correspond exactly to what was
Packit ae235b
 * received on the wire.
Packit ae235b
 *
Packit ae235b
 * This is typically used when presenting errors to the end user.
Packit ae235b
 *
Packit ae235b
 * Returns: %TRUE if information was stripped, %FALSE otherwise.
Packit ae235b
 *
Packit ae235b
 * Since: 2.26
Packit ae235b
 */
Packit ae235b
gboolean
Packit ae235b
g_dbus_error_strip_remote_error (GError *error)
Packit ae235b
{
Packit ae235b
  gboolean ret;
Packit ae235b
Packit ae235b
  g_return_val_if_fail (error != NULL, FALSE);
Packit ae235b
Packit ae235b
  ret = FALSE;
Packit ae235b
Packit ae235b
  if (g_str_has_prefix (error->message, "GDBus.Error:"))
Packit ae235b
    {
Packit ae235b
      const gchar *begin;
Packit ae235b
      const gchar *end;
Packit ae235b
      gchar *new_message;
Packit ae235b
Packit ae235b
      begin = error->message + sizeof ("GDBus.Error:") -1;
Packit ae235b
      end = strstr (begin, ":");
Packit ae235b
      if (end != NULL && end[1] == ' ')
Packit ae235b
        {
Packit ae235b
          new_message = g_strdup (end + 2);
Packit ae235b
          g_free (error->message);
Packit ae235b
          error->message = new_message;
Packit ae235b
          ret = TRUE;
Packit ae235b
        }
Packit ae235b
    }
Packit ae235b
Packit ae235b
  return ret;
Packit ae235b
}
Packit ae235b
Packit ae235b
/**
Packit ae235b
 * g_dbus_error_encode_gerror:
Packit ae235b
 * @error: A #GError.
Packit ae235b
 *
Packit ae235b
 * Creates a D-Bus error name to use for @error. If @error matches
Packit ae235b
 * a registered error (cf. g_dbus_error_register_error()), the corresponding
Packit ae235b
 * D-Bus error name will be returned.
Packit ae235b
 *
Packit ae235b
 * Otherwise the a name of the form
Packit ae235b
 * `org.gtk.GDBus.UnmappedGError.Quark._ESCAPED_QUARK_NAME.Code_ERROR_CODE`
Packit ae235b
 * will be used. This allows other GDBus applications to map the error
Packit ae235b
 * on the wire back to a #GError using g_dbus_error_new_for_dbus_error().
Packit ae235b
 *
Packit ae235b
 * This function is typically only used in object mappings to put a
Packit ae235b
 * #GError on the wire. Regular applications should not use it.
Packit ae235b
 *
Packit ae235b
 * Returns: A D-Bus error name (never %NULL). Free with g_free().
Packit ae235b
 *
Packit ae235b
 * Since: 2.26
Packit ae235b
 */
Packit ae235b
gchar *
Packit ae235b
g_dbus_error_encode_gerror (const GError *error)
Packit ae235b
{
Packit ae235b
  RegisteredError *re;
Packit ae235b
  gchar *error_name;
Packit ae235b
Packit ae235b
  g_return_val_if_fail (error != NULL, NULL);
Packit ae235b
Packit ae235b
  /* Ensure that e.g. G_DBUS_ERROR is registered using g_dbus_error_register_error() */
Packit ae235b
  _g_dbus_initialize ();
Packit ae235b
Packit ae235b
  error_name = NULL;
Packit ae235b
Packit ae235b
  G_LOCK (error_lock);
Packit ae235b
  re = NULL;
Packit ae235b
  if (quark_code_pair_to_re != NULL)
Packit ae235b
    {
Packit ae235b
      QuarkCodePair pair;
Packit ae235b
      pair.error_domain = error->domain;
Packit ae235b
      pair.error_code = error->code;
Packit ae235b
      g_assert (dbus_error_name_to_re != NULL); /* check invariant */
Packit ae235b
      re = g_hash_table_lookup (quark_code_pair_to_re, &pair);
Packit ae235b
    }
Packit ae235b
  if (re != NULL)
Packit ae235b
    {
Packit ae235b
      error_name = g_strdup (re->dbus_error_name);
Packit ae235b
      G_UNLOCK (error_lock);
Packit ae235b
    }
Packit ae235b
  else
Packit ae235b
    {
Packit ae235b
      const gchar *domain_as_string;
Packit ae235b
      GString *s;
Packit ae235b
      guint n;
Packit ae235b
Packit ae235b
      G_UNLOCK (error_lock);
Packit ae235b
Packit ae235b
      /* We can't make a lot of assumptions about what domain_as_string
Packit ae235b
       * looks like and D-Bus is extremely picky about error names so
Packit ae235b
       * hex-encode it for transport across the wire.
Packit ae235b
       */
Packit ae235b
      domain_as_string = g_quark_to_string (error->domain);
Packit ae235b
Packit ae235b
      /* 0 is not a domain; neither are non-quark integers */
Packit ae235b
      g_return_val_if_fail (domain_as_string != NULL, NULL);
Packit ae235b
Packit ae235b
      s = g_string_new ("org.gtk.GDBus.UnmappedGError.Quark._");
Packit ae235b
      for (n = 0; domain_as_string[n] != 0; n++)
Packit ae235b
        {
Packit ae235b
          gint c = domain_as_string[n];
Packit ae235b
          if (g_ascii_isalnum (c))
Packit ae235b
            {
Packit ae235b
              g_string_append_c (s, c);
Packit ae235b
            }
Packit ae235b
          else
Packit ae235b
            {
Packit ae235b
              guint nibble_top;
Packit ae235b
              guint nibble_bottom;
Packit ae235b
              g_string_append_c (s, '_');
Packit ae235b
              nibble_top = ((int) domain_as_string[n]) >> 4;
Packit ae235b
              nibble_bottom = ((int) domain_as_string[n]) & 0x0f;
Packit ae235b
              if (nibble_top < 10)
Packit ae235b
                nibble_top += '0';
Packit ae235b
              else
Packit ae235b
                nibble_top += 'a' - 10;
Packit ae235b
              if (nibble_bottom < 10)
Packit ae235b
                nibble_bottom += '0';
Packit ae235b
              else
Packit ae235b
                nibble_bottom += 'a' - 10;
Packit ae235b
              g_string_append_c (s, nibble_top);
Packit ae235b
              g_string_append_c (s, nibble_bottom);
Packit ae235b
            }
Packit ae235b
        }
Packit ae235b
      g_string_append_printf (s, ".Code%d", error->code);
Packit ae235b
      error_name = g_string_free (s, FALSE);
Packit ae235b
    }
Packit ae235b
Packit ae235b
  return error_name;
Packit ae235b
}