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