Coverage Report

Created: 2025-09-27 07:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pango/subprojects/glib/gio/gdbuserror.c
Line
Count
Source
1
/* GDBus - GLib D-Bus Library
2
 *
3
 * Copyright (C) 2008-2010 Red Hat, Inc.
4
 *
5
 * SPDX-License-Identifier: LGPL-2.1-or-later
6
 *
7
 * This library is free software; you can redistribute it and/or
8
 * modify it under the terms of the GNU Lesser General Public
9
 * License as published by the Free Software Foundation; either
10
 * version 2.1 of the License, or (at your option) any later version.
11
 *
12
 * This library is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15
 * Lesser General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Lesser General
18
 * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
19
 *
20
 * Author: David Zeuthen <davidz@redhat.com>
21
 */
22
23
#include "config.h"
24
25
#include <stdlib.h>
26
#include <string.h>
27
28
#include "gdbuserror.h"
29
#include "gioenums.h"
30
#include "gioenumtypes.h"
31
#include "gioerror.h"
32
#include "gdbusprivate.h"
33
34
#include "glibintl.h"
35
36
static const GDBusErrorEntry g_dbus_error_entries[] =
37
{
38
  {G_DBUS_ERROR_FAILED,                           DBUS_ERROR_FAILED},
39
  {G_DBUS_ERROR_NO_MEMORY,                        "org.freedesktop.DBus.Error.NoMemory"},
40
  {G_DBUS_ERROR_SERVICE_UNKNOWN,                  "org.freedesktop.DBus.Error.ServiceUnknown"},
41
  {G_DBUS_ERROR_NAME_HAS_NO_OWNER,                DBUS_ERROR_NAME_HAS_NO_OWNER},
42
  {G_DBUS_ERROR_NO_REPLY,                         "org.freedesktop.DBus.Error.NoReply"},
43
  {G_DBUS_ERROR_IO_ERROR,                         "org.freedesktop.DBus.Error.IOError"},
44
  {G_DBUS_ERROR_BAD_ADDRESS,                      "org.freedesktop.DBus.Error.BadAddress"},
45
  {G_DBUS_ERROR_NOT_SUPPORTED,                    "org.freedesktop.DBus.Error.NotSupported"},
46
  {G_DBUS_ERROR_LIMITS_EXCEEDED,                  "org.freedesktop.DBus.Error.LimitsExceeded"},
47
  {G_DBUS_ERROR_ACCESS_DENIED,                    "org.freedesktop.DBus.Error.AccessDenied"},
48
  {G_DBUS_ERROR_AUTH_FAILED,                      "org.freedesktop.DBus.Error.AuthFailed"},
49
  {G_DBUS_ERROR_NO_SERVER,                        "org.freedesktop.DBus.Error.NoServer"},
50
  {G_DBUS_ERROR_TIMEOUT,                          "org.freedesktop.DBus.Error.Timeout"},
51
  {G_DBUS_ERROR_NO_NETWORK,                       "org.freedesktop.DBus.Error.NoNetwork"},
52
  {G_DBUS_ERROR_ADDRESS_IN_USE,                   "org.freedesktop.DBus.Error.AddressInUse"},
53
  {G_DBUS_ERROR_DISCONNECTED,                     "org.freedesktop.DBus.Error.Disconnected"},
54
  {G_DBUS_ERROR_INVALID_ARGS,                     DBUS_ERROR_INVALID_ARGS},
55
  {G_DBUS_ERROR_FILE_NOT_FOUND,                   "org.freedesktop.DBus.Error.FileNotFound"},
56
  {G_DBUS_ERROR_FILE_EXISTS,                      "org.freedesktop.DBus.Error.FileExists"},
57
  {G_DBUS_ERROR_UNKNOWN_METHOD,                   DBUS_ERROR_UNKNOWN_METHOD},
58
  {G_DBUS_ERROR_TIMED_OUT,                        "org.freedesktop.DBus.Error.TimedOut"},
59
  {G_DBUS_ERROR_MATCH_RULE_NOT_FOUND,             "org.freedesktop.DBus.Error.MatchRuleNotFound"},
60
  {G_DBUS_ERROR_MATCH_RULE_INVALID,               "org.freedesktop.DBus.Error.MatchRuleInvalid"},
61
  {G_DBUS_ERROR_SPAWN_EXEC_FAILED,                "org.freedesktop.DBus.Error.Spawn.ExecFailed"},
62
  {G_DBUS_ERROR_SPAWN_FORK_FAILED,                "org.freedesktop.DBus.Error.Spawn.ForkFailed"},
63
  {G_DBUS_ERROR_SPAWN_CHILD_EXITED,               "org.freedesktop.DBus.Error.Spawn.ChildExited"},
64
  {G_DBUS_ERROR_SPAWN_CHILD_SIGNALED,             "org.freedesktop.DBus.Error.Spawn.ChildSignaled"},
65
  {G_DBUS_ERROR_SPAWN_FAILED,                     "org.freedesktop.DBus.Error.Spawn.Failed"},
66
  {G_DBUS_ERROR_SPAWN_SETUP_FAILED,               "org.freedesktop.DBus.Error.Spawn.FailedToSetup"},
67
  {G_DBUS_ERROR_SPAWN_CONFIG_INVALID,             "org.freedesktop.DBus.Error.Spawn.ConfigInvalid"},
68
  {G_DBUS_ERROR_SPAWN_SERVICE_INVALID,            "org.freedesktop.DBus.Error.Spawn.ServiceNotValid"},
69
  {G_DBUS_ERROR_SPAWN_SERVICE_NOT_FOUND,          "org.freedesktop.DBus.Error.Spawn.ServiceNotFound"},
70
  {G_DBUS_ERROR_SPAWN_PERMISSIONS_INVALID,        "org.freedesktop.DBus.Error.Spawn.PermissionsInvalid"},
71
  {G_DBUS_ERROR_SPAWN_FILE_INVALID,               "org.freedesktop.DBus.Error.Spawn.FileInvalid"},
72
  {G_DBUS_ERROR_SPAWN_NO_MEMORY,                  "org.freedesktop.DBus.Error.Spawn.NoMemory"},
73
  {G_DBUS_ERROR_UNIX_PROCESS_ID_UNKNOWN,          "org.freedesktop.DBus.Error.UnixProcessIdUnknown"},
74
  {G_DBUS_ERROR_INVALID_SIGNATURE,                "org.freedesktop.DBus.Error.InvalidSignature"},
75
  {G_DBUS_ERROR_INVALID_FILE_CONTENT,             "org.freedesktop.DBus.Error.InvalidFileContent"},
76
  {G_DBUS_ERROR_SELINUX_SECURITY_CONTEXT_UNKNOWN, "org.freedesktop.DBus.Error.SELinuxSecurityContextUnknown"},
77
  {G_DBUS_ERROR_ADT_AUDIT_DATA_UNKNOWN,           "org.freedesktop.DBus.Error.AdtAuditDataUnknown"},
78
  {G_DBUS_ERROR_OBJECT_PATH_IN_USE,               "org.freedesktop.DBus.Error.ObjectPathInUse"},
79
  {G_DBUS_ERROR_UNKNOWN_OBJECT,                   "org.freedesktop.DBus.Error.UnknownObject"},
80
  {G_DBUS_ERROR_UNKNOWN_INTERFACE,                "org.freedesktop.DBus.Error.UnknownInterface"},
81
  {G_DBUS_ERROR_UNKNOWN_PROPERTY,                 "org.freedesktop.DBus.Error.UnknownProperty"},
82
  {G_DBUS_ERROR_PROPERTY_READ_ONLY,               "org.freedesktop.DBus.Error.PropertyReadOnly"},
83
};
84
85
GQuark
86
g_dbus_error_quark (void)
87
0
{
88
0
  G_STATIC_ASSERT (G_N_ELEMENTS (g_dbus_error_entries) - 1 == G_DBUS_ERROR_PROPERTY_READ_ONLY);
89
0
  static gsize quark = 0;
90
0
  g_dbus_error_register_error_domain ("g-dbus-error-quark",
91
0
                                      &quark,
92
0
                                      g_dbus_error_entries,
93
0
                                      G_N_ELEMENTS (g_dbus_error_entries));
94
0
  return (GQuark) quark;
95
0
}
96
97
/**
98
 * g_dbus_error_register_error_domain:
99
 * @error_domain_quark_name: the error domain name
100
 * @quark_volatile: (out): return location for the [type@GLib.Quark] representing the
101
 *   error domain
102
 * @entries: (array length=num_entries): items to register
103
 * @num_entries: number of items to register
104
 *
105
 * Helper function for associating a [type@GLib.Error] error domain with D-Bus
106
 * error names.
107
 *
108
 * While @quark_volatile has a `volatile` qualifier, this is a historical
109
 * artifact and the argument passed to it should not be `volatile`.
110
 *
111
 * Since: 2.26
112
 */
113
void
114
g_dbus_error_register_error_domain (const gchar           *error_domain_quark_name,
115
                                    volatile gsize        *quark_volatile,
116
                                    const GDBusErrorEntry *entries,
117
                                    guint                  num_entries)
118
0
{
119
0
  gsize *quark;
120
121
0
  g_return_if_fail (error_domain_quark_name != NULL);
122
0
  g_return_if_fail (quark_volatile != NULL);
123
0
  g_return_if_fail (entries != NULL);
124
0
  g_return_if_fail (num_entries > 0);
125
126
  /* Drop the volatile qualifier, which should never have been on the argument
127
   * in the first place. */
128
0
  quark = (gsize *) quark_volatile;
129
130
0
  if (g_once_init_enter (quark))
131
0
    {
132
0
      guint n;
133
0
      GQuark new_quark;
134
135
0
      new_quark = g_quark_from_static_string (error_domain_quark_name);
136
137
0
      for (n = 0; n < num_entries; n++)
138
0
        {
139
0
          g_warn_if_fail (g_dbus_error_register_error (new_quark,
140
0
                                                       entries[n].error_code,
141
0
                                                       entries[n].dbus_error_name));
142
0
        }
143
0
      g_once_init_leave (quark, new_quark);
144
0
    }
145
0
}
146
147
static gboolean
148
_g_dbus_error_decode_gerror (const gchar *dbus_name,
149
                             GQuark      *out_error_domain,
150
                             gint        *out_error_code)
151
0
{
152
0
  gboolean ret;
153
0
  guint n;
154
0
  GString *s;
155
0
  gchar *domain_quark_string;
156
157
0
  ret = FALSE;
158
0
  s = NULL;
159
160
0
  if (g_str_has_prefix (dbus_name, "org.gtk.GDBus.UnmappedGError.Quark._"))
161
0
    {
162
0
      s = g_string_new (NULL);
163
164
0
      for (n = sizeof "org.gtk.GDBus.UnmappedGError.Quark._" - 1;
165
0
           dbus_name[n] != '.' && dbus_name[n] != '\0';
166
0
           n++)
167
0
        {
168
0
          if (g_ascii_isalnum (dbus_name[n]))
169
0
            {
170
0
              g_string_append_c (s, dbus_name[n]);
171
0
            }
172
0
          else if (dbus_name[n] == '_')
173
0
            {
174
0
              guint nibble_top;
175
0
              guint nibble_bottom;
176
177
0
              n++;
178
179
0
              nibble_top = dbus_name[n];
180
0
              if (nibble_top >= '0' && nibble_top <= '9')
181
0
                nibble_top -= '0';
182
0
              else if (nibble_top >= 'a' && nibble_top <= 'f')
183
0
                nibble_top -= ('a' - 10);
184
0
              else
185
0
                goto not_mapped;
186
187
0
              n++;
188
189
0
              nibble_bottom = dbus_name[n];
190
0
              if (nibble_bottom >= '0' && nibble_bottom <= '9')
191
0
                nibble_bottom -= '0';
192
0
              else if (nibble_bottom >= 'a' && nibble_bottom <= 'f')
193
0
                nibble_bottom -= ('a' - 10);
194
0
              else
195
0
                goto not_mapped;
196
197
0
              g_string_append_c (s, (nibble_top<<4) | nibble_bottom);
198
0
            }
199
0
          else
200
0
            {
201
0
              goto not_mapped;
202
0
            }
203
0
        }
204
205
0
      if (!g_str_has_prefix (dbus_name + n, ".Code"))
206
0
        goto not_mapped;
207
208
0
      domain_quark_string = g_string_free (s, FALSE);
209
0
      s = NULL;
210
211
0
      if (out_error_domain != NULL)
212
0
        *out_error_domain = g_quark_from_string (domain_quark_string);
213
0
      g_free (domain_quark_string);
214
215
0
      if (out_error_code != NULL)
216
0
        *out_error_code = atoi (dbus_name + n + sizeof ".Code" - 1);
217
218
0
      ret = TRUE;
219
0
    }
220
221
0
 not_mapped:
222
223
0
  if (s != NULL)
224
0
    g_string_free (s, TRUE);
225
226
0
  return ret;
227
0
}
228
229
/* ---------------------------------------------------------------------------------------------------- */
230
231
typedef struct
232
{
233
  GQuark error_domain;
234
  gint   error_code;
235
} QuarkCodePair;
236
237
static guint
238
quark_code_pair_hash_func (const QuarkCodePair *pair)
239
0
{
240
0
  gint val;
241
0
  val = pair->error_domain + pair->error_code;
242
0
  return g_int_hash (&val);
243
0
}
244
245
static gboolean
246
quark_code_pair_equal_func (const QuarkCodePair *a,
247
                            const QuarkCodePair *b)
248
0
{
249
0
  return (a->error_domain == b->error_domain) && (a->error_code == b->error_code);
250
0
}
251
252
typedef struct
253
{
254
  QuarkCodePair pair;
255
  gchar *dbus_error_name;
256
} RegisteredError;
257
258
static void
259
registered_error_free (RegisteredError *re)
260
0
{
261
0
  g_free (re->dbus_error_name);
262
0
  g_free (re);
263
0
}
264
265
G_LOCK_DEFINE_STATIC (error_lock);
266
267
/* maps from QuarkCodePair* -> RegisteredError* */
268
static GHashTable *quark_code_pair_to_re = NULL;
269
270
/* maps from gchar* -> RegisteredError* */
271
static GHashTable *dbus_error_name_to_re = NULL;
272
273
/**
274
 * g_dbus_error_register_error:
275
 * @error_domain: a [type@GLib.Quark] for an error domain
276
 * @error_code: an error code
277
 * @dbus_error_name: a D-Bus error name
278
 *
279
 * Creates an association mapping between @dbus_error_name and
280
 * [type@GLib.Error]s specified by @error_domain and @error_code.
281
 *
282
 * This is typically done in the function that returns the [type@GLib.Quark] for
283
 * an error domain.
284
 *
285
 * Returns: true if the association was created, false if it already
286
 *   exists
287
 * Since: 2.26
288
 */
289
gboolean
290
g_dbus_error_register_error (GQuark       error_domain,
291
                             gint         error_code,
292
                             const gchar *dbus_error_name)
293
0
{
294
0
  gboolean ret;
295
0
  QuarkCodePair pair;
296
0
  RegisteredError *re;
297
298
0
  g_return_val_if_fail (dbus_error_name != NULL, FALSE);
299
300
0
  ret = FALSE;
301
302
0
  G_LOCK (error_lock);
303
304
0
  if (quark_code_pair_to_re == NULL)
305
0
    {
306
0
      g_assert (dbus_error_name_to_re == NULL); /* check invariant */
307
0
      quark_code_pair_to_re = g_hash_table_new ((GHashFunc) quark_code_pair_hash_func,
308
0
                                                (GEqualFunc) quark_code_pair_equal_func);
309
0
      dbus_error_name_to_re = g_hash_table_new_full (g_str_hash,
310
0
                                                     g_str_equal,
311
0
                                                     NULL,
312
0
                                                     (GDestroyNotify) registered_error_free);
313
0
    }
314
315
0
  if (g_hash_table_lookup (dbus_error_name_to_re, dbus_error_name) != NULL)
316
0
    goto out;
317
318
0
  pair.error_domain = error_domain;
319
0
  pair.error_code = error_code;
320
321
0
  if (g_hash_table_lookup (quark_code_pair_to_re, &pair) != NULL)
322
0
    goto out;
323
324
0
  re = g_new0 (RegisteredError, 1);
325
0
  re->pair = pair;
326
0
  re->dbus_error_name = g_strdup (dbus_error_name);
327
328
0
  g_hash_table_insert (quark_code_pair_to_re, &(re->pair), re);
329
0
  g_hash_table_insert (dbus_error_name_to_re, re->dbus_error_name, re);
330
331
0
  ret = TRUE;
332
333
0
 out:
334
0
  G_UNLOCK (error_lock);
335
0
  return ret;
336
0
}
337
338
/**
339
 * g_dbus_error_unregister_error:
340
 * @error_domain: a [type@GLib.Quark] for an error domain
341
 * @error_code: an error code
342
 * @dbus_error_name: a D-Bus error name
343
 *
344
 * Destroys an association previously set up with
345
 * [func@Gio.DBusError.register_error].
346
 *
347
 * Returns: true if the association was destroyed, false if it wasn’t found
348
 * Since: 2.26
349
 */
350
gboolean
351
g_dbus_error_unregister_error (GQuark       error_domain,
352
                               gint         error_code,
353
                               const gchar *dbus_error_name)
354
0
{
355
0
  gboolean ret;
356
0
  RegisteredError *re;
357
0
  guint hash_size;
358
359
0
  g_return_val_if_fail (dbus_error_name != NULL, FALSE);
360
361
0
  ret = FALSE;
362
363
0
  G_LOCK (error_lock);
364
365
0
  if (dbus_error_name_to_re == NULL)
366
0
    {
367
0
      g_assert (quark_code_pair_to_re == NULL); /* check invariant */
368
0
      goto out;
369
0
    }
370
371
0
  re = g_hash_table_lookup (dbus_error_name_to_re, dbus_error_name);
372
0
  if (re == NULL)
373
0
    {
374
0
      QuarkCodePair pair;
375
0
      pair.error_domain = error_domain;
376
0
      pair.error_code = error_code;
377
0
      g_warn_if_fail (g_hash_table_lookup (quark_code_pair_to_re, &pair) == NULL); /* check invariant */
378
0
      goto out;
379
0
    }
380
381
0
  ret = TRUE;
382
383
0
  g_warn_if_fail (g_hash_table_lookup (quark_code_pair_to_re, &(re->pair)) == re); /* check invariant */
384
385
0
  g_warn_if_fail (g_hash_table_remove (quark_code_pair_to_re, &(re->pair)));
386
0
  g_warn_if_fail (g_hash_table_remove (dbus_error_name_to_re, re->dbus_error_name));
387
388
  /* destroy hashes if empty */
389
0
  hash_size = g_hash_table_size (dbus_error_name_to_re);
390
0
  if (hash_size == 0)
391
0
    {
392
0
      g_warn_if_fail (g_hash_table_size (quark_code_pair_to_re) == 0); /* check invariant */
393
394
0
      g_hash_table_unref (dbus_error_name_to_re);
395
0
      dbus_error_name_to_re = NULL;
396
0
      g_hash_table_unref (quark_code_pair_to_re);
397
0
      quark_code_pair_to_re = NULL;
398
0
    }
399
0
  else
400
0
    {
401
0
      g_warn_if_fail (g_hash_table_size (quark_code_pair_to_re) == hash_size); /* check invariant */
402
0
    }
403
404
0
 out:
405
0
  G_UNLOCK (error_lock);
406
0
  return ret;
407
0
}
408
409
/* ---------------------------------------------------------------------------------------------------- */
410
411
/**
412
 * g_dbus_error_is_remote_error:
413
 * @error: an error
414
 *
415
 * Checks if @error represents an error received via D-Bus from a remote peer.
416
 *
417
 * If so, use [func@Gio.DBusError.get_remote_error] to get the name of the error.
418
 *
419
 * Returns: true if @error represents an error from a remote peer; false otherwise
420
 * Since: 2.26
421
 */
422
gboolean
423
g_dbus_error_is_remote_error (const GError *error)
424
0
{
425
0
  g_return_val_if_fail (error != NULL, FALSE);
426
0
  return g_str_has_prefix (error->message, "GDBus.Error:");
427
0
}
428
429
430
/**
431
 * g_dbus_error_get_remote_error:
432
 * @error: an error
433
 *
434
 * Gets the D-Bus error name used for @error, if any.
435
 *
436
 * This function is guaranteed to return a D-Bus error name for all
437
 * [type@GLib.Error]s returned from functions handling remote method calls
438
 * (for example, [method@Gio.DBusConnection.call_finish]) unless
439
 * [func@Gio.DBusError.strip_remote_error] has already been used on @error.
440
 *
441
 * Returns: (nullable) (transfer full): an allocated string, or `NULL` if the
442
 *   D-Bus error name could not be found
443
 * Since: 2.26
444
 */
445
gchar *
446
g_dbus_error_get_remote_error (const GError *error)
447
0
{
448
0
  RegisteredError *re;
449
0
  gchar *ret;
450
451
0
  g_return_val_if_fail (error != NULL, NULL);
452
453
  /* Ensure that e.g. G_DBUS_ERROR is registered using g_dbus_error_register_error() */
454
0
  _g_dbus_initialize ();
455
456
0
  ret = NULL;
457
458
0
  G_LOCK (error_lock);
459
460
0
  re = NULL;
461
0
  if (quark_code_pair_to_re != NULL)
462
0
    {
463
0
      QuarkCodePair pair;
464
0
      pair.error_domain = error->domain;
465
0
      pair.error_code = error->code;
466
0
      g_assert (dbus_error_name_to_re != NULL); /* check invariant */
467
0
      re = g_hash_table_lookup (quark_code_pair_to_re, &pair);
468
0
    }
469
470
0
  if (re != NULL)
471
0
    {
472
0
      ret = g_strdup (re->dbus_error_name);
473
0
    }
474
0
  else
475
0
    {
476
0
      if (g_str_has_prefix (error->message, "GDBus.Error:"))
477
0
        {
478
0
          const gchar *begin;
479
0
          const gchar *end;
480
0
          begin = error->message + sizeof ("GDBus.Error:") -1;
481
0
          end = strstr (begin, ":");
482
0
          if (end != NULL && end[1] == ' ')
483
0
            {
484
0
              ret = g_strndup (begin, end - begin);
485
0
            }
486
0
        }
487
0
    }
488
489
0
  G_UNLOCK (error_lock);
490
491
0
  return ret;
492
0
}
493
494
/* ---------------------------------------------------------------------------------------------------- */
495
496
/**
497
 * g_dbus_error_new_for_dbus_error:
498
 * @dbus_error_name: D-Bus error name
499
 * @dbus_error_message: D-Bus error message
500
 *
501
 * Creates a [type@GLib.Error] based on the contents of @dbus_error_name and
502
 * @dbus_error_message.
503
 *
504
 * Errors registered with [func@Gio.DBusError.register_error] will be looked
505
 * up using @dbus_error_name and if a match is found, the error domain
506
 * and code is used. Applications can use [func@Gio.DBusError.get_remote_error]
507
 * to recover @dbus_error_name.
508
 *
509
 * If a match against a registered error is not found and the D-Bus
510
 * error name is in a form as returned by [func@Gio.DBusError.encode_gerror]
511
 * the error domain and code encoded in the name is used to
512
 * create the [type@GLib.Error]. Also, @dbus_error_name is added to the error
513
 * message such that it can be recovered with
514
 * [func@Gio.DBusError.get_remote_error].
515
 *
516
 * Otherwise, a [type@GLib.Error] with the error code
517
 * [error@Gio.IOErrorEnum.DBUS_ERROR]
518
 * in the [error@Gio.IOErrorEnum] error domain is returned. Also, @dbus_error_name is
519
 * added to the error message such that it can be recovered with
520
 * [func@Gio.DBusError.get_remote_error].
521
 *
522
 * In all three cases, @dbus_error_name can always be recovered from the
523
 * returned [type@GLib.Error] using the [func@Gio.DBusError.get_remote_error]
524
 * function (unless [func@Gio.DBusError.strip_remote_error] hasn’t been used on
525
 * the returned error).
526
 *
527
 * This function is typically only used in object mappings to prepare
528
 * [type@GLib.Error] instances for applications. Regular applications should not
529
 * use it.
530
 *
531
 * Returns: (transfer full): an allocated [type@GLib.Error]
532
 * Since: 2.26
533
 */
534
GError *
535
g_dbus_error_new_for_dbus_error (const gchar *dbus_error_name,
536
                                 const gchar *dbus_error_message)
537
0
{
538
0
  GError *error;
539
0
  RegisteredError *re;
540
541
0
  g_return_val_if_fail (dbus_error_name != NULL, NULL);
542
0
  g_return_val_if_fail (dbus_error_message != NULL, NULL);
543
544
  /* Ensure that e.g. G_DBUS_ERROR is registered using g_dbus_error_register_error() */
545
0
  _g_dbus_initialize ();
546
547
0
  G_LOCK (error_lock);
548
549
0
  re = NULL;
550
0
  if (dbus_error_name_to_re != NULL)
551
0
    {
552
0
      g_assert (quark_code_pair_to_re != NULL); /* check invariant */
553
0
      re = g_hash_table_lookup (dbus_error_name_to_re, dbus_error_name);
554
0
    }
555
556
0
  if (re != NULL)
557
0
    {
558
0
      error = g_error_new (re->pair.error_domain,
559
0
                           re->pair.error_code,
560
0
                           "GDBus.Error:%s: %s",
561
0
                           dbus_error_name,
562
0
                           dbus_error_message);
563
0
    }
564
0
  else
565
0
    {
566
0
      GQuark error_domain = 0;
567
0
      gint error_code = 0;
568
569
0
      if (_g_dbus_error_decode_gerror (dbus_error_name,
570
0
                                       &error_domain,
571
0
                                       &error_code))
572
0
        {
573
0
          error = g_error_new (error_domain,
574
0
                               error_code,
575
0
                               "GDBus.Error:%s: %s",
576
0
                               dbus_error_name,
577
0
                               dbus_error_message);
578
0
        }
579
0
      else
580
0
        {
581
0
          error = g_error_new (G_IO_ERROR,
582
0
                               G_IO_ERROR_DBUS_ERROR,
583
0
                               "GDBus.Error:%s: %s",
584
0
                               dbus_error_name,
585
0
                               dbus_error_message);
586
0
        }
587
0
    }
588
589
0
  G_UNLOCK (error_lock);
590
0
  return error;
591
0
}
592
593
/**
594
 * g_dbus_error_set_dbus_error:
595
 * @error: (out callee-allocates) (optional) (nullable): return location for
596
 *   a [type@GLib.Error]
597
 * @dbus_error_name: D-Bus error name
598
 * @dbus_error_message: D-Bus error message
599
 * @format: (nullable): `printf()`-style format to prepend to
600
 *   @dbus_error_message, or `NULL` to not modify the message
601
 * @...: arguments for @format
602
 *
603
 * Sets `*error` to a new [type@GLib.Error] created with
604
 * [func@Gio.DBusError.new_for_dbus_error].
605
 *
606
 * If @format is non-`NULL` then it is prepended to @dbus_error_message.
607
 * Otherwise @dbus_error_message is used unmodified.
608
 *
609
 * This function does nothing if @error is `NULL`.
610
 *
611
 * Since: 2.26
612
 */
613
void
614
g_dbus_error_set_dbus_error (GError      **error,
615
                             const gchar  *dbus_error_name,
616
                             const gchar  *dbus_error_message,
617
                             const gchar  *format,
618
                             ...)
619
0
{
620
0
  g_return_if_fail (error == NULL || *error == NULL);
621
0
  g_return_if_fail (dbus_error_name != NULL);
622
0
  g_return_if_fail (dbus_error_message != NULL);
623
624
0
  if (error == NULL)
625
0
    return;
626
627
0
  if (format == NULL)
628
0
    {
629
0
      *error = g_dbus_error_new_for_dbus_error (dbus_error_name, dbus_error_message);
630
0
    }
631
0
  else
632
0
    {
633
0
      va_list var_args;
634
0
      va_start (var_args, format);
635
0
      g_dbus_error_set_dbus_error_valist (error,
636
0
                                          dbus_error_name,
637
0
                                          dbus_error_message,
638
0
                                          format,
639
0
                                          var_args);
640
0
      va_end (var_args);
641
0
    }
642
0
}
643
644
/**
645
 * g_dbus_error_set_dbus_error_valist:
646
 * @error: (out callee-allocates) (optional) (nullable): return location for
647
 *   a [type@GLib.Error]
648
 * @dbus_error_name: D-Bus error name
649
 * @dbus_error_message: D-Bus error message
650
 * @format: (nullable): `printf()`-style format to prepend to
651
 *   @dbus_error_message, or `NULL` to not modify the message
652
 * @var_args: arguments for @format
653
 *
654
 * Like [func@Gio.DBusError.set_dbus_error] but intended for language bindings.
655
 *
656
 * Since: 2.26
657
 */
658
void
659
g_dbus_error_set_dbus_error_valist (GError      **error,
660
                                    const gchar  *dbus_error_name,
661
                                    const gchar  *dbus_error_message,
662
                                    const gchar  *format,
663
                                    va_list       var_args)
664
0
{
665
0
  g_return_if_fail (error == NULL || *error == NULL);
666
0
  g_return_if_fail (dbus_error_name != NULL);
667
0
  g_return_if_fail (dbus_error_message != NULL);
668
669
0
  if (error == NULL)
670
0
    return;
671
672
0
  if (format != NULL)
673
0
    {
674
0
      gchar *message;
675
0
      gchar *s;
676
0
      message = g_strdup_vprintf (format, var_args);
677
0
      s = g_strdup_printf ("%s: %s", message, dbus_error_message);
678
0
      *error = g_dbus_error_new_for_dbus_error (dbus_error_name, s);
679
0
      g_free (s);
680
0
      g_free (message);
681
0
    }
682
0
  else
683
0
    {
684
0
      *error = g_dbus_error_new_for_dbus_error (dbus_error_name, dbus_error_message);
685
0
    }
686
0
}
687
688
/**
689
 * g_dbus_error_strip_remote_error:
690
 * @error: an error
691
 *
692
 * Looks for extra information in the error message used to recover
693
 * the D-Bus error name and strips it if found.
694
 *
695
 * If stripped, the
696
 * message field in @error will correspond exactly to what was
697
 * received on the wire.
698
 *
699
 * This is typically used when presenting errors to the end user.
700
 *
701
 * Returns: true if information was stripped; false otherwise
702
 * Since: 2.26
703
 */
704
gboolean
705
g_dbus_error_strip_remote_error (GError *error)
706
0
{
707
0
  gboolean ret;
708
709
0
  g_return_val_if_fail (error != NULL, FALSE);
710
711
0
  ret = FALSE;
712
713
0
  if (g_str_has_prefix (error->message, "GDBus.Error:"))
714
0
    {
715
0
      const gchar *begin;
716
0
      const gchar *end;
717
0
      gchar *new_message;
718
719
0
      begin = error->message + sizeof ("GDBus.Error:") -1;
720
0
      end = strstr (begin, ":");
721
0
      if (end != NULL && end[1] == ' ')
722
0
        {
723
0
          new_message = g_strdup (end + 2);
724
0
          g_free (error->message);
725
0
          error->message = new_message;
726
0
          ret = TRUE;
727
0
        }
728
0
    }
729
730
0
  return ret;
731
0
}
732
733
/**
734
 * g_dbus_error_encode_gerror:
735
 * @error: an error
736
 *
737
 * Creates a D-Bus error name to use for @error.
738
 *
739
 * If @error matches a registered error (see
740
 * [func@Gio.DBusError.register_error]), the corresponding D-Bus error name
741
 * will be returned.
742
 *
743
 * Otherwise the a name of the form
744
 * `org.gtk.GDBus.UnmappedGError.Quark._ESCAPED_QUARK_NAME.Code_ERROR_CODE`
745
 * will be used. This allows other GDBus applications to map the error
746
 * on the wire back to a [type@GLib.Error] using
747
 * [func@Gio.DBusError.new_for_dbus_error].
748
 *
749
 * This function is typically only used in object mappings to put a
750
 * [type@GLib.Error] on the wire. Regular applications should not use it.
751
 *
752
 * Returns: (transfer full) (not nullable): a D-Bus error name
753
 * Since: 2.26
754
 */
755
gchar *
756
g_dbus_error_encode_gerror (const GError *error)
757
0
{
758
0
  RegisteredError *re;
759
0
  gchar *error_name;
760
761
0
  g_return_val_if_fail (error != NULL, NULL);
762
763
  /* Ensure that e.g. G_DBUS_ERROR is registered using g_dbus_error_register_error() */
764
0
  _g_dbus_initialize ();
765
766
0
  error_name = NULL;
767
768
0
  G_LOCK (error_lock);
769
0
  re = NULL;
770
0
  if (quark_code_pair_to_re != NULL)
771
0
    {
772
0
      QuarkCodePair pair;
773
0
      pair.error_domain = error->domain;
774
0
      pair.error_code = error->code;
775
0
      g_assert (dbus_error_name_to_re != NULL); /* check invariant */
776
0
      re = g_hash_table_lookup (quark_code_pair_to_re, &pair);
777
0
    }
778
0
  if (re != NULL)
779
0
    {
780
0
      error_name = g_strdup (re->dbus_error_name);
781
0
      G_UNLOCK (error_lock);
782
0
    }
783
0
  else
784
0
    {
785
0
      const gchar *domain_as_string;
786
0
      GString *s;
787
0
      guint n;
788
789
0
      G_UNLOCK (error_lock);
790
791
      /* We can't make a lot of assumptions about what domain_as_string
792
       * looks like and D-Bus is extremely picky about error names so
793
       * hex-encode it for transport across the wire.
794
       */
795
0
      domain_as_string = g_quark_to_string (error->domain);
796
797
      /* 0 is not a domain; neither are non-quark integers */
798
0
      g_return_val_if_fail (domain_as_string != NULL, NULL);
799
800
0
      s = g_string_new ("org.gtk.GDBus.UnmappedGError.Quark._");
801
0
      for (n = 0; domain_as_string[n] != 0; n++)
802
0
        {
803
0
          gint c = domain_as_string[n];
804
0
          if (g_ascii_isalnum (c))
805
0
            {
806
0
              g_string_append_c (s, c);
807
0
            }
808
0
          else
809
0
            {
810
0
              guint nibble_top;
811
0
              guint nibble_bottom;
812
0
              g_string_append_c (s, '_');
813
0
              nibble_top = ((int) domain_as_string[n]) >> 4;
814
0
              nibble_bottom = ((int) domain_as_string[n]) & 0x0f;
815
0
              if (nibble_top < 10)
816
0
                nibble_top += '0';
817
0
              else
818
0
                nibble_top += 'a' - 10;
819
0
              if (nibble_bottom < 10)
820
0
                nibble_bottom += '0';
821
0
              else
822
0
                nibble_bottom += 'a' - 10;
823
0
              g_string_append_c (s, nibble_top);
824
0
              g_string_append_c (s, nibble_bottom);
825
0
            }
826
0
        }
827
0
      g_string_append_printf (s, ".Code%d", error->code);
828
0
      error_name = g_string_free (s, FALSE);
829
0
    }
830
831
0
  return error_name;
832
0
}