Coverage Report

Created: 2025-07-23 08:13

/src/pango/subprojects/glib/gio/gdbuserror.c
Line
Count
Source (jump to first uncovered line)
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: A pointer where to store the #GQuark.
101
 * @entries: (array length=num_entries): A pointer to @num_entries #GDBusErrorEntry struct items.
102
 * @num_entries: Number of items to register.
103
 *
104
 * Helper function for associating a #GError error domain with D-Bus error names.
105
 *
106
 * While @quark_volatile has a `volatile` qualifier, this is a historical
107
 * artifact and the argument passed to it should not be `volatile`.
108
 *
109
 * Since: 2.26
110
 */
111
void
112
g_dbus_error_register_error_domain (const gchar           *error_domain_quark_name,
113
                                    volatile gsize        *quark_volatile,
114
                                    const GDBusErrorEntry *entries,
115
                                    guint                  num_entries)
116
0
{
117
0
  gsize *quark;
118
119
0
  g_return_if_fail (error_domain_quark_name != NULL);
120
0
  g_return_if_fail (quark_volatile != NULL);
121
0
  g_return_if_fail (entries != NULL);
122
0
  g_return_if_fail (num_entries > 0);
123
124
  /* Drop the volatile qualifier, which should never have been on the argument
125
   * in the first place. */
126
0
  quark = (gsize *) quark_volatile;
127
128
0
  if (g_once_init_enter (quark))
129
0
    {
130
0
      guint n;
131
0
      GQuark new_quark;
132
133
0
      new_quark = g_quark_from_static_string (error_domain_quark_name);
134
135
0
      for (n = 0; n < num_entries; n++)
136
0
        {
137
0
          g_warn_if_fail (g_dbus_error_register_error (new_quark,
138
0
                                                       entries[n].error_code,
139
0
                                                       entries[n].dbus_error_name));
140
0
        }
141
0
      g_once_init_leave (quark, new_quark);
142
0
    }
143
0
}
144
145
static gboolean
146
_g_dbus_error_decode_gerror (const gchar *dbus_name,
147
                             GQuark      *out_error_domain,
148
                             gint        *out_error_code)
149
0
{
150
0
  gboolean ret;
151
0
  guint n;
152
0
  GString *s;
153
0
  gchar *domain_quark_string;
154
155
0
  ret = FALSE;
156
0
  s = NULL;
157
158
0
  if (g_str_has_prefix (dbus_name, "org.gtk.GDBus.UnmappedGError.Quark._"))
159
0
    {
160
0
      s = g_string_new (NULL);
161
162
0
      for (n = sizeof "org.gtk.GDBus.UnmappedGError.Quark._" - 1;
163
0
           dbus_name[n] != '.' && dbus_name[n] != '\0';
164
0
           n++)
165
0
        {
166
0
          if (g_ascii_isalnum (dbus_name[n]))
167
0
            {
168
0
              g_string_append_c (s, dbus_name[n]);
169
0
            }
170
0
          else if (dbus_name[n] == '_')
171
0
            {
172
0
              guint nibble_top;
173
0
              guint nibble_bottom;
174
175
0
              n++;
176
177
0
              nibble_top = dbus_name[n];
178
0
              if (nibble_top >= '0' && nibble_top <= '9')
179
0
                nibble_top -= '0';
180
0
              else if (nibble_top >= 'a' && nibble_top <= 'f')
181
0
                nibble_top -= ('a' - 10);
182
0
              else
183
0
                goto not_mapped;
184
185
0
              n++;
186
187
0
              nibble_bottom = dbus_name[n];
188
0
              if (nibble_bottom >= '0' && nibble_bottom <= '9')
189
0
                nibble_bottom -= '0';
190
0
              else if (nibble_bottom >= 'a' && nibble_bottom <= 'f')
191
0
                nibble_bottom -= ('a' - 10);
192
0
              else
193
0
                goto not_mapped;
194
195
0
              g_string_append_c (s, (nibble_top<<4) | nibble_bottom);
196
0
            }
197
0
          else
198
0
            {
199
0
              goto not_mapped;
200
0
            }
201
0
        }
202
203
0
      if (!g_str_has_prefix (dbus_name + n, ".Code"))
204
0
        goto not_mapped;
205
206
0
      domain_quark_string = g_string_free (s, FALSE);
207
0
      s = NULL;
208
209
0
      if (out_error_domain != NULL)
210
0
        *out_error_domain = g_quark_from_string (domain_quark_string);
211
0
      g_free (domain_quark_string);
212
213
0
      if (out_error_code != NULL)
214
0
        *out_error_code = atoi (dbus_name + n + sizeof ".Code" - 1);
215
216
0
      ret = TRUE;
217
0
    }
218
219
0
 not_mapped:
220
221
0
  if (s != NULL)
222
0
    g_string_free (s, TRUE);
223
224
0
  return ret;
225
0
}
226
227
/* ---------------------------------------------------------------------------------------------------- */
228
229
typedef struct
230
{
231
  GQuark error_domain;
232
  gint   error_code;
233
} QuarkCodePair;
234
235
static guint
236
quark_code_pair_hash_func (const QuarkCodePair *pair)
237
0
{
238
0
  gint val;
239
0
  val = pair->error_domain + pair->error_code;
240
0
  return g_int_hash (&val);
241
0
}
242
243
static gboolean
244
quark_code_pair_equal_func (const QuarkCodePair *a,
245
                            const QuarkCodePair *b)
246
0
{
247
0
  return (a->error_domain == b->error_domain) && (a->error_code == b->error_code);
248
0
}
249
250
typedef struct
251
{
252
  QuarkCodePair pair;
253
  gchar *dbus_error_name;
254
} RegisteredError;
255
256
static void
257
registered_error_free (RegisteredError *re)
258
0
{
259
0
  g_free (re->dbus_error_name);
260
0
  g_free (re);
261
0
}
262
263
G_LOCK_DEFINE_STATIC (error_lock);
264
265
/* maps from QuarkCodePair* -> RegisteredError* */
266
static GHashTable *quark_code_pair_to_re = NULL;
267
268
/* maps from gchar* -> RegisteredError* */
269
static GHashTable *dbus_error_name_to_re = NULL;
270
271
/**
272
 * g_dbus_error_register_error:
273
 * @error_domain: A #GQuark for an error domain.
274
 * @error_code: An error code.
275
 * @dbus_error_name: A D-Bus error name.
276
 *
277
 * Creates an association to map between @dbus_error_name and
278
 * #GErrors specified by @error_domain and @error_code.
279
 *
280
 * This is typically done in the routine that returns the #GQuark for
281
 * an error domain.
282
 *
283
 * Returns: %TRUE if the association was created, %FALSE if it already
284
 * exists.
285
 *
286
 * Since: 2.26
287
 */
288
gboolean
289
g_dbus_error_register_error (GQuark       error_domain,
290
                             gint         error_code,
291
                             const gchar *dbus_error_name)
292
0
{
293
0
  gboolean ret;
294
0
  QuarkCodePair pair;
295
0
  RegisteredError *re;
296
297
0
  g_return_val_if_fail (dbus_error_name != NULL, FALSE);
298
299
0
  ret = FALSE;
300
301
0
  G_LOCK (error_lock);
302
303
0
  if (quark_code_pair_to_re == NULL)
304
0
    {
305
0
      g_assert (dbus_error_name_to_re == NULL); /* check invariant */
306
0
      quark_code_pair_to_re = g_hash_table_new ((GHashFunc) quark_code_pair_hash_func,
307
0
                                                (GEqualFunc) quark_code_pair_equal_func);
308
0
      dbus_error_name_to_re = g_hash_table_new_full (g_str_hash,
309
0
                                                     g_str_equal,
310
0
                                                     NULL,
311
0
                                                     (GDestroyNotify) registered_error_free);
312
0
    }
313
314
0
  if (g_hash_table_lookup (dbus_error_name_to_re, dbus_error_name) != NULL)
315
0
    goto out;
316
317
0
  pair.error_domain = error_domain;
318
0
  pair.error_code = error_code;
319
320
0
  if (g_hash_table_lookup (quark_code_pair_to_re, &pair) != NULL)
321
0
    goto out;
322
323
0
  re = g_new0 (RegisteredError, 1);
324
0
  re->pair = pair;
325
0
  re->dbus_error_name = g_strdup (dbus_error_name);
326
327
0
  g_hash_table_insert (quark_code_pair_to_re, &(re->pair), re);
328
0
  g_hash_table_insert (dbus_error_name_to_re, re->dbus_error_name, re);
329
330
0
  ret = TRUE;
331
332
0
 out:
333
0
  G_UNLOCK (error_lock);
334
0
  return ret;
335
0
}
336
337
/**
338
 * g_dbus_error_unregister_error:
339
 * @error_domain: A #GQuark for an error domain.
340
 * @error_code: An error code.
341
 * @dbus_error_name: A D-Bus error name.
342
 *
343
 * Destroys an association previously set up with g_dbus_error_register_error().
344
 *
345
 * Returns: %TRUE if the association was destroyed, %FALSE if it wasn't found.
346
 *
347
 * Since: 2.26
348
 */
349
gboolean
350
g_dbus_error_unregister_error (GQuark       error_domain,
351
                               gint         error_code,
352
                               const gchar *dbus_error_name)
353
0
{
354
0
  gboolean ret;
355
0
  RegisteredError *re;
356
0
  guint hash_size;
357
358
0
  g_return_val_if_fail (dbus_error_name != NULL, FALSE);
359
360
0
  ret = FALSE;
361
362
0
  G_LOCK (error_lock);
363
364
0
  if (dbus_error_name_to_re == NULL)
365
0
    {
366
0
      g_assert (quark_code_pair_to_re == NULL); /* check invariant */
367
0
      goto out;
368
0
    }
369
370
0
  re = g_hash_table_lookup (dbus_error_name_to_re, dbus_error_name);
371
0
  if (re == NULL)
372
0
    {
373
0
      QuarkCodePair pair;
374
0
      pair.error_domain = error_domain;
375
0
      pair.error_code = error_code;
376
0
      g_warn_if_fail (g_hash_table_lookup (quark_code_pair_to_re, &pair) == NULL); /* check invariant */
377
0
      goto out;
378
0
    }
379
380
0
  ret = TRUE;
381
382
0
  g_warn_if_fail (g_hash_table_lookup (quark_code_pair_to_re, &(re->pair)) == re); /* check invariant */
383
384
0
  g_warn_if_fail (g_hash_table_remove (quark_code_pair_to_re, &(re->pair)));
385
0
  g_warn_if_fail (g_hash_table_remove (dbus_error_name_to_re, re->dbus_error_name));
386
387
  /* destroy hashes if empty */
388
0
  hash_size = g_hash_table_size (dbus_error_name_to_re);
389
0
  if (hash_size == 0)
390
0
    {
391
0
      g_warn_if_fail (g_hash_table_size (quark_code_pair_to_re) == 0); /* check invariant */
392
393
0
      g_hash_table_unref (dbus_error_name_to_re);
394
0
      dbus_error_name_to_re = NULL;
395
0
      g_hash_table_unref (quark_code_pair_to_re);
396
0
      quark_code_pair_to_re = NULL;
397
0
    }
398
0
  else
399
0
    {
400
0
      g_warn_if_fail (g_hash_table_size (quark_code_pair_to_re) == hash_size); /* check invariant */
401
0
    }
402
403
0
 out:
404
0
  G_UNLOCK (error_lock);
405
0
  return ret;
406
0
}
407
408
/* ---------------------------------------------------------------------------------------------------- */
409
410
/**
411
 * g_dbus_error_is_remote_error:
412
 * @error: A #GError.
413
 *
414
 * Checks if @error represents an error received via D-Bus from a remote peer. If so,
415
 * use g_dbus_error_get_remote_error() to get the name of the error.
416
 *
417
 * Returns: %TRUE if @error represents an error from a remote peer,
418
 * %FALSE otherwise.
419
 *
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: a #GError
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
 * #GErrors returned from functions handling remote method calls
438
 * (e.g. g_dbus_connection_call_finish()) unless
439
 * g_dbus_error_strip_remote_error() has 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. Free with g_free().
443
 *
444
 * Since: 2.26
445
 */
446
gchar *
447
g_dbus_error_get_remote_error (const GError *error)
448
0
{
449
0
  RegisteredError *re;
450
0
  gchar *ret;
451
452
0
  g_return_val_if_fail (error != NULL, NULL);
453
454
  /* Ensure that e.g. G_DBUS_ERROR is registered using g_dbus_error_register_error() */
455
0
  _g_dbus_initialize ();
456
457
0
  ret = NULL;
458
459
0
  G_LOCK (error_lock);
460
461
0
  re = NULL;
462
0
  if (quark_code_pair_to_re != NULL)
463
0
    {
464
0
      QuarkCodePair pair;
465
0
      pair.error_domain = error->domain;
466
0
      pair.error_code = error->code;
467
0
      g_assert (dbus_error_name_to_re != NULL); /* check invariant */
468
0
      re = g_hash_table_lookup (quark_code_pair_to_re, &pair);
469
0
    }
470
471
0
  if (re != NULL)
472
0
    {
473
0
      ret = g_strdup (re->dbus_error_name);
474
0
    }
475
0
  else
476
0
    {
477
0
      if (g_str_has_prefix (error->message, "GDBus.Error:"))
478
0
        {
479
0
          const gchar *begin;
480
0
          const gchar *end;
481
0
          begin = error->message + sizeof ("GDBus.Error:") -1;
482
0
          end = strstr (begin, ":");
483
0
          if (end != NULL && end[1] == ' ')
484
0
            {
485
0
              ret = g_strndup (begin, end - begin);
486
0
            }
487
0
        }
488
0
    }
489
490
0
  G_UNLOCK (error_lock);
491
492
0
  return ret;
493
0
}
494
495
/* ---------------------------------------------------------------------------------------------------- */
496
497
/**
498
 * g_dbus_error_new_for_dbus_error:
499
 * @dbus_error_name: D-Bus error name.
500
 * @dbus_error_message: D-Bus error message.
501
 *
502
 * Creates a #GError based on the contents of @dbus_error_name and
503
 * @dbus_error_message.
504
 *
505
 * Errors registered with g_dbus_error_register_error() will be looked
506
 * up using @dbus_error_name and if a match is found, the error domain
507
 * and code is used. Applications can use g_dbus_error_get_remote_error()
508
 * to recover @dbus_error_name.
509
 *
510
 * If a match against a registered error is not found and the D-Bus
511
 * error name is in a form as returned by g_dbus_error_encode_gerror()
512
 * the error domain and code encoded in the name is used to
513
 * create the #GError. Also, @dbus_error_name is added to the error message
514
 * such that it can be recovered with g_dbus_error_get_remote_error().
515
 *
516
 * Otherwise, a #GError with the error code %G_IO_ERROR_DBUS_ERROR
517
 * in the %G_IO_ERROR error domain is returned. Also, @dbus_error_name is
518
 * added to the error message such that it can be recovered with
519
 * g_dbus_error_get_remote_error().
520
 *
521
 * In all three cases, @dbus_error_name can always be recovered from the
522
 * returned #GError using the g_dbus_error_get_remote_error() function
523
 * (unless g_dbus_error_strip_remote_error() hasn't been used on the returned error).
524
 *
525
 * This function is typically only used in object mappings to prepare
526
 * #GError instances for applications. Regular applications should not use
527
 * it.
528
 *
529
 * Returns: (transfer full): An allocated #GError. Free with g_error_free().
530
 *
531
 * Since: 2.26
532
 */
533
GError *
534
g_dbus_error_new_for_dbus_error (const gchar *dbus_error_name,
535
                                 const gchar *dbus_error_message)
536
0
{
537
0
  GError *error;
538
0
  RegisteredError *re;
539
540
0
  g_return_val_if_fail (dbus_error_name != NULL, NULL);
541
0
  g_return_val_if_fail (dbus_error_message != NULL, NULL);
542
543
  /* Ensure that e.g. G_DBUS_ERROR is registered using g_dbus_error_register_error() */
544
0
  _g_dbus_initialize ();
545
546
0
  G_LOCK (error_lock);
547
548
0
  re = NULL;
549
0
  if (dbus_error_name_to_re != NULL)
550
0
    {
551
0
      g_assert (quark_code_pair_to_re != NULL); /* check invariant */
552
0
      re = g_hash_table_lookup (dbus_error_name_to_re, dbus_error_name);
553
0
    }
554
555
0
  if (re != NULL)
556
0
    {
557
0
      error = g_error_new (re->pair.error_domain,
558
0
                           re->pair.error_code,
559
0
                           "GDBus.Error:%s: %s",
560
0
                           dbus_error_name,
561
0
                           dbus_error_message);
562
0
    }
563
0
  else
564
0
    {
565
0
      GQuark error_domain = 0;
566
0
      gint error_code = 0;
567
568
0
      if (_g_dbus_error_decode_gerror (dbus_error_name,
569
0
                                       &error_domain,
570
0
                                       &error_code))
571
0
        {
572
0
          error = g_error_new (error_domain,
573
0
                               error_code,
574
0
                               "GDBus.Error:%s: %s",
575
0
                               dbus_error_name,
576
0
                               dbus_error_message);
577
0
        }
578
0
      else
579
0
        {
580
0
          error = g_error_new (G_IO_ERROR,
581
0
                               G_IO_ERROR_DBUS_ERROR,
582
0
                               "GDBus.Error:%s: %s",
583
0
                               dbus_error_name,
584
0
                               dbus_error_message);
585
0
        }
586
0
    }
587
588
0
  G_UNLOCK (error_lock);
589
0
  return error;
590
0
}
591
592
/**
593
 * g_dbus_error_set_dbus_error:
594
 * @error: A pointer to a #GError or %NULL.
595
 * @dbus_error_name: D-Bus error name.
596
 * @dbus_error_message: D-Bus error message.
597
 * @format: (nullable): printf()-style format to prepend to @dbus_error_message or %NULL.
598
 * @...: Arguments for @format.
599
 *
600
 * Does nothing if @error is %NULL. Otherwise sets `*error` to
601
 * a new #GError created with g_dbus_error_new_for_dbus_error()
602
 * with @dbus_error_message prepend with @format (unless %NULL).
603
 *
604
 * Since: 2.26
605
 */
606
void
607
g_dbus_error_set_dbus_error (GError      **error,
608
                             const gchar  *dbus_error_name,
609
                             const gchar  *dbus_error_message,
610
                             const gchar  *format,
611
                             ...)
612
0
{
613
0
  g_return_if_fail (error == NULL || *error == NULL);
614
0
  g_return_if_fail (dbus_error_name != NULL);
615
0
  g_return_if_fail (dbus_error_message != NULL);
616
617
0
  if (error == NULL)
618
0
    return;
619
620
0
  if (format == NULL)
621
0
    {
622
0
      *error = g_dbus_error_new_for_dbus_error (dbus_error_name, dbus_error_message);
623
0
    }
624
0
  else
625
0
    {
626
0
      va_list var_args;
627
0
      va_start (var_args, format);
628
0
      g_dbus_error_set_dbus_error_valist (error,
629
0
                                          dbus_error_name,
630
0
                                          dbus_error_message,
631
0
                                          format,
632
0
                                          var_args);
633
0
      va_end (var_args);
634
0
    }
635
0
}
636
637
/**
638
 * g_dbus_error_set_dbus_error_valist:
639
 * @error: A pointer to a #GError or %NULL.
640
 * @dbus_error_name: D-Bus error name.
641
 * @dbus_error_message: D-Bus error message.
642
 * @format: (nullable): printf()-style format to prepend to @dbus_error_message or %NULL.
643
 * @var_args: Arguments for @format.
644
 *
645
 * Like g_dbus_error_set_dbus_error() but intended for language bindings.
646
 *
647
 * Since: 2.26
648
 */
649
void
650
g_dbus_error_set_dbus_error_valist (GError      **error,
651
                                    const gchar  *dbus_error_name,
652
                                    const gchar  *dbus_error_message,
653
                                    const gchar  *format,
654
                                    va_list       var_args)
655
0
{
656
0
  g_return_if_fail (error == NULL || *error == NULL);
657
0
  g_return_if_fail (dbus_error_name != NULL);
658
0
  g_return_if_fail (dbus_error_message != NULL);
659
660
0
  if (error == NULL)
661
0
    return;
662
663
0
  if (format != NULL)
664
0
    {
665
0
      gchar *message;
666
0
      gchar *s;
667
0
      message = g_strdup_vprintf (format, var_args);
668
0
      s = g_strdup_printf ("%s: %s", message, dbus_error_message);
669
0
      *error = g_dbus_error_new_for_dbus_error (dbus_error_name, s);
670
0
      g_free (s);
671
0
      g_free (message);
672
0
    }
673
0
  else
674
0
    {
675
0
      *error = g_dbus_error_new_for_dbus_error (dbus_error_name, dbus_error_message);
676
0
    }
677
0
}
678
679
/**
680
 * g_dbus_error_strip_remote_error:
681
 * @error: A #GError.
682
 *
683
 * Looks for extra information in the error message used to recover
684
 * the D-Bus error name and strips it if found. If stripped, the
685
 * message field in @error will correspond exactly to what was
686
 * received on the wire.
687
 *
688
 * This is typically used when presenting errors to the end user.
689
 *
690
 * Returns: %TRUE if information was stripped, %FALSE otherwise.
691
 *
692
 * Since: 2.26
693
 */
694
gboolean
695
g_dbus_error_strip_remote_error (GError *error)
696
0
{
697
0
  gboolean ret;
698
699
0
  g_return_val_if_fail (error != NULL, FALSE);
700
701
0
  ret = FALSE;
702
703
0
  if (g_str_has_prefix (error->message, "GDBus.Error:"))
704
0
    {
705
0
      const gchar *begin;
706
0
      const gchar *end;
707
0
      gchar *new_message;
708
709
0
      begin = error->message + sizeof ("GDBus.Error:") -1;
710
0
      end = strstr (begin, ":");
711
0
      if (end != NULL && end[1] == ' ')
712
0
        {
713
0
          new_message = g_strdup (end + 2);
714
0
          g_free (error->message);
715
0
          error->message = new_message;
716
0
          ret = TRUE;
717
0
        }
718
0
    }
719
720
0
  return ret;
721
0
}
722
723
/**
724
 * g_dbus_error_encode_gerror:
725
 * @error: A #GError.
726
 *
727
 * Creates a D-Bus error name to use for @error. If @error matches
728
 * a registered error (cf. g_dbus_error_register_error()), the corresponding
729
 * D-Bus error name will be returned.
730
 *
731
 * Otherwise the a name of the form
732
 * `org.gtk.GDBus.UnmappedGError.Quark._ESCAPED_QUARK_NAME.Code_ERROR_CODE`
733
 * will be used. This allows other GDBus applications to map the error
734
 * on the wire back to a #GError using g_dbus_error_new_for_dbus_error().
735
 *
736
 * This function is typically only used in object mappings to put a
737
 * #GError on the wire. Regular applications should not use it.
738
 *
739
 * Returns: (transfer full) (not nullable): A D-Bus error name (never %NULL).
740
 *     Free with g_free().
741
 *
742
 * Since: 2.26
743
 */
744
gchar *
745
g_dbus_error_encode_gerror (const GError *error)
746
0
{
747
0
  RegisteredError *re;
748
0
  gchar *error_name;
749
750
0
  g_return_val_if_fail (error != NULL, NULL);
751
752
  /* Ensure that e.g. G_DBUS_ERROR is registered using g_dbus_error_register_error() */
753
0
  _g_dbus_initialize ();
754
755
0
  error_name = NULL;
756
757
0
  G_LOCK (error_lock);
758
0
  re = NULL;
759
0
  if (quark_code_pair_to_re != NULL)
760
0
    {
761
0
      QuarkCodePair pair;
762
0
      pair.error_domain = error->domain;
763
0
      pair.error_code = error->code;
764
0
      g_assert (dbus_error_name_to_re != NULL); /* check invariant */
765
0
      re = g_hash_table_lookup (quark_code_pair_to_re, &pair);
766
0
    }
767
0
  if (re != NULL)
768
0
    {
769
0
      error_name = g_strdup (re->dbus_error_name);
770
0
      G_UNLOCK (error_lock);
771
0
    }
772
0
  else
773
0
    {
774
0
      const gchar *domain_as_string;
775
0
      GString *s;
776
0
      guint n;
777
778
0
      G_UNLOCK (error_lock);
779
780
      /* We can't make a lot of assumptions about what domain_as_string
781
       * looks like and D-Bus is extremely picky about error names so
782
       * hex-encode it for transport across the wire.
783
       */
784
0
      domain_as_string = g_quark_to_string (error->domain);
785
786
      /* 0 is not a domain; neither are non-quark integers */
787
0
      g_return_val_if_fail (domain_as_string != NULL, NULL);
788
789
0
      s = g_string_new ("org.gtk.GDBus.UnmappedGError.Quark._");
790
0
      for (n = 0; domain_as_string[n] != 0; n++)
791
0
        {
792
0
          gint c = domain_as_string[n];
793
0
          if (g_ascii_isalnum (c))
794
0
            {
795
0
              g_string_append_c (s, c);
796
0
            }
797
0
          else
798
0
            {
799
0
              guint nibble_top;
800
0
              guint nibble_bottom;
801
0
              g_string_append_c (s, '_');
802
0
              nibble_top = ((int) domain_as_string[n]) >> 4;
803
0
              nibble_bottom = ((int) domain_as_string[n]) & 0x0f;
804
0
              if (nibble_top < 10)
805
0
                nibble_top += '0';
806
0
              else
807
0
                nibble_top += 'a' - 10;
808
0
              if (nibble_bottom < 10)
809
0
                nibble_bottom += '0';
810
0
              else
811
0
                nibble_bottom += 'a' - 10;
812
0
              g_string_append_c (s, nibble_top);
813
0
              g_string_append_c (s, nibble_bottom);
814
0
            }
815
0
        }
816
0
      g_string_append_printf (s, ".Code%d", error->code);
817
0
      error_name = g_string_free (s, FALSE);
818
0
    }
819
820
0
  return error_name;
821
0
}