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