1 /*
2  * dbus.c - Source for D-Bus utilities
3  *
4  * Copyright (C) 2005-2008 Collabora Ltd. <http://www.collabora.co.uk/>
5  * Copyright (C) 2005-2008 Nokia Corporation
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 Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20  */
21 
22 /**
23  * SECTION:dbus
24  * @title: D-Bus utilities
25  * @short_description: some D-Bus utility functions
26  *
27  * D-Bus utility functions used in telepathy-glib.
28  */
29 
30 /**
31  * SECTION:asv
32  * @title: Manipulating a{sv} mappings
33  * @short_description: Functions to manipulate mappings from string to
34  *  variant, as represented in dbus-glib by a #GHashTable from string
35  *  to #GValue
36  *
37  * Mappings from string to variant (D-Bus signature a{sv}) are commonly used
38  * to provide extensibility, but in dbus-glib they're somewhat awkward to deal
39  * with.
40  *
41  * These functions provide convenient access to the values in such
42  * a mapping.
43  *
44  * They also work around the fact that none of the #GHashTable public API
45  * takes a const pointer to a #GHashTable, even the read-only methods that
46  * logically ought to.
47  *
48  * Parts of telepathy-glib return const pointers to #GHashTable, to encourage
49  * the use of this API.
50  *
51  * Since: 0.7.9
52  */
53 
54 #include "config.h"
55 
56 #include <telepathy-glib/dbus.h>
57 #include <telepathy-glib/dbus-internal.h>
58 
59 #include <stdlib.h>
60 #include <string.h>
61 
62 #include <dbus/dbus.h>
63 
64 #include <gobject/gvaluecollector.h>
65 
66 #include <telepathy-glib/errors.h>
67 #include <telepathy-glib/gtypes.h>
68 #include <telepathy-glib/util.h>
69 
70 #define DEBUG_FLAG TP_DEBUG_MISC
71 #include "debug-internal.h"
72 
73 /**
74  * tp_asv_size: (skip)
75  * @asv: a GHashTable
76  *
77  * Return the size of @asv as if via g_hash_table_size().
78  *
79  * The only difference is that this version takes a const #GHashTable and
80  * casts it.
81  *
82  * Since: 0.7.12
83  */
84 /* (#define + static inline in dbus.h) */
85 
86 /**
87  * tp_dbus_g_method_return_not_implemented: (skip)
88  * @context: The D-Bus method invocation context
89  *
90  * Return the Telepathy error NotImplemented from the method invocation
91  * given by @context.
92  */
93 void
tp_dbus_g_method_return_not_implemented(DBusGMethodInvocation * context)94 tp_dbus_g_method_return_not_implemented (DBusGMethodInvocation *context)
95 {
96   GError e = { TP_ERROR, TP_ERROR_NOT_IMPLEMENTED, "Not implemented" };
97 
98   dbus_g_method_return_error (context, &e);
99 }
100 
101 DBusGConnection *
_tp_dbus_starter_bus_conn(GError ** error)102 _tp_dbus_starter_bus_conn (GError **error)
103 {
104   static DBusGConnection *starter_bus = NULL;
105 
106   if (starter_bus == NULL)
107     {
108       starter_bus = dbus_g_bus_get (DBUS_BUS_STARTER, error);
109     }
110 
111   return starter_bus;
112 }
113 
114 /**
115  * tp_get_bus: (skip)
116  *
117  * Returns a connection to the D-Bus daemon on which this process was
118  * activated if it was launched by D-Bus service activation, or the session
119  * bus otherwise.
120  *
121  * If dbus_g_bus_get() fails, exit with error code 1.
122  *
123  * Note that this function is not suitable for use in applications which can
124  * be useful even in the absence of D-Bus - it is designed for use in
125  * connection managers, which are not at all useful without a D-Bus
126  * connection. See &lt;https://bugs.freedesktop.org/show_bug.cgi?id=18832&gt;.
127  * Most processes should use tp_dbus_daemon_dup() instead.
128  *
129  * Returns: a connection to the starter or session D-Bus daemon.
130  */
131 DBusGConnection *
tp_get_bus(void)132 tp_get_bus (void)
133 {
134   GError *error = NULL;
135   DBusGConnection *bus = _tp_dbus_starter_bus_conn (&error);
136 
137   if (bus == NULL)
138     {
139       WARNING ("Failed to connect to starter bus: %s", error->message);
140       exit (1);
141     }
142 
143   return bus;
144 }
145 
146 /**
147  * tp_get_bus_proxy: (skip)
148  *
149  * Return a #DBusGProxy for the bus daemon object. The same caveats as for
150  * tp_get_bus() apply.
151  *
152  * Returns: a proxy for the bus daemon object on the starter or session bus.
153  *
154  * Deprecated: 0.7.26: Use tp_dbus_daemon_dup() in new code.
155  */
156 DBusGProxy *
tp_get_bus_proxy(void)157 tp_get_bus_proxy (void)
158 {
159   static DBusGProxy *bus_proxy = NULL;
160 
161   if (bus_proxy == NULL)
162     {
163       GError *error = NULL;
164       DBusGConnection *bus = _tp_dbus_starter_bus_conn (&error);
165 
166       if (bus == NULL)
167         {
168           WARNING ("Failed to connect to starter bus: %s", error->message);
169           exit (1);
170         }
171 
172       bus_proxy = dbus_g_proxy_new_for_name (bus,
173                                             "org.freedesktop.DBus",
174                                             "/org/freedesktop/DBus",
175                                             "org.freedesktop.DBus");
176 
177       if (bus_proxy == NULL)
178         ERROR ("Failed to get proxy object for bus.");
179     }
180 
181   return bus_proxy;
182 }
183 
184 /**
185  * TpDBusNameType:
186  * @TP_DBUS_NAME_TYPE_UNIQUE: accept unique names like :1.123
187  *  (not including the name of the bus daemon itself)
188  * @TP_DBUS_NAME_TYPE_WELL_KNOWN: accept well-known names like
189  *  com.example.Service (not including the name of the bus daemon itself)
190  * @TP_DBUS_NAME_TYPE_BUS_DAEMON: accept the name of the bus daemon
191  *  itself, which has the syntax of a well-known name, but behaves like a
192  *  unique name
193  * @TP_DBUS_NAME_TYPE_NOT_BUS_DAEMON: accept either unique or well-known
194  *  names, but not the bus daemon
195  * @TP_DBUS_NAME_TYPE_ANY: accept any of the above
196  *
197  * A set of flags indicating which D-Bus bus names are acceptable.
198  * They can be combined with the bitwise-or operator to accept multiple
199  * types. %TP_DBUS_NAME_TYPE_NOT_BUS_DAEMON and %TP_DBUS_NAME_TYPE_ANY are
200  * the bitwise-or of other appropriate types, for convenience.
201  *
202  * Since 0.11.5, there is a corresponding #GFlagsClass type,
203  * %TP_TYPE_DBUS_NAME_TYPE.
204  *
205  * Since: 0.7.1
206  */
207 
208 /**
209  * TP_TYPE_DBUS_NAME_TYPE:
210  *
211  * The #GFlagsClass type of a #TpDBusNameType or a set of name types.
212  *
213  * Since: 0.11.5
214  */
215 
216 /**
217  * tp_dbus_check_valid_bus_name:
218  * @name: a possible bus name
219  * @allow_types: some combination of %TP_DBUS_NAME_TYPE_UNIQUE,
220  *  %TP_DBUS_NAME_TYPE_WELL_KNOWN or %TP_DBUS_NAME_TYPE_BUS_DAEMON
221  *  (often this will be %TP_DBUS_NAME_TYPE_NOT_BUS_DAEMON or
222  *  %TP_DBUS_NAME_TYPE_ANY)
223  * @error: used to raise %TP_DBUS_ERROR_INVALID_BUS_NAME if %FALSE is returned
224  *
225  * Check that the given string is a valid D-Bus bus name of an appropriate
226  * type.
227  *
228  * Returns: %TRUE if @name is valid
229  *
230  * Since: 0.7.1
231  */
232 gboolean
tp_dbus_check_valid_bus_name(const gchar * name,TpDBusNameType allow_types,GError ** error)233 tp_dbus_check_valid_bus_name (const gchar *name,
234                               TpDBusNameType allow_types,
235                               GError **error)
236 {
237   gboolean dot = FALSE;
238   gboolean unique;
239   gchar last;
240   const gchar *ptr;
241 
242   g_return_val_if_fail (name != NULL, FALSE);
243 
244   if (name[0] == '\0')
245     {
246       g_set_error (error, TP_DBUS_ERRORS, TP_DBUS_ERROR_INVALID_BUS_NAME,
247           "The empty string is not a valid bus name");
248       return FALSE;
249     }
250 
251   if (!tp_strdiff (name, DBUS_SERVICE_DBUS))
252     {
253       if (allow_types & TP_DBUS_NAME_TYPE_BUS_DAEMON)
254         return TRUE;
255 
256       g_set_error (error, TP_DBUS_ERRORS, TP_DBUS_ERROR_INVALID_BUS_NAME,
257           "The D-Bus daemon's bus name is not acceptable here");
258       return FALSE;
259     }
260 
261   unique = (name[0] == ':');
262   if (unique && (allow_types & TP_DBUS_NAME_TYPE_UNIQUE) == 0)
263     {
264       g_set_error (error, TP_DBUS_ERRORS, TP_DBUS_ERROR_INVALID_BUS_NAME,
265           "A well-known bus name not starting with ':'%s is required",
266           allow_types & TP_DBUS_NAME_TYPE_BUS_DAEMON
267             ? " (or the bus daemon itself)"
268             : "");
269       return FALSE;
270     }
271 
272   if (!unique && (allow_types & TP_DBUS_NAME_TYPE_WELL_KNOWN) == 0)
273     {
274       g_set_error (error, TP_DBUS_ERRORS, TP_DBUS_ERROR_INVALID_BUS_NAME,
275           "A unique bus name starting with ':'%s is required",
276           allow_types & TP_DBUS_NAME_TYPE_BUS_DAEMON
277             ? " (or the bus daemon itself)"
278             : "");
279       return FALSE;
280     }
281 
282   if (strlen (name) > 255)
283     {
284       g_set_error (error, TP_DBUS_ERRORS, TP_DBUS_ERROR_INVALID_BUS_NAME,
285           "Invalid bus name: too long (> 255 characters)");
286       return FALSE;
287     }
288 
289   last = '\0';
290 
291   for (ptr = name + (unique ? 1 : 0); *ptr != '\0'; ptr++)
292     {
293       if (*ptr == '.')
294         {
295           dot = TRUE;
296 
297           if (last == '.')
298             {
299               g_set_error (error, TP_DBUS_ERRORS,
300                   TP_DBUS_ERROR_INVALID_BUS_NAME,
301                   "Invalid bus name '%s': contains '..'", name);
302               return FALSE;
303             }
304           else if (last == '\0')
305             {
306               g_set_error (error, TP_DBUS_ERRORS,
307                   TP_DBUS_ERROR_INVALID_BUS_NAME,
308                   "Invalid bus name '%s': must not start with '.'", name);
309               return FALSE;
310             }
311         }
312       else if (g_ascii_isdigit (*ptr))
313         {
314           if (!unique)
315             {
316               if (last == '.')
317                 {
318                   g_set_error (error, TP_DBUS_ERRORS,
319                       TP_DBUS_ERROR_INVALID_BUS_NAME,
320                       "Invalid bus name '%s': a digit may not follow '.' "
321                       "except in a unique name starting with ':'", name);
322                   return FALSE;
323                 }
324               else if (last == '\0')
325                 {
326                   g_set_error (error, TP_DBUS_ERRORS,
327                       TP_DBUS_ERROR_INVALID_BUS_NAME,
328                       "Invalid bus name '%s': must not start with a digit",
329                       name);
330                   return FALSE;
331                 }
332             }
333         }
334       else if (!g_ascii_isalpha (*ptr) && *ptr != '_' && *ptr != '-')
335         {
336           g_set_error (error, TP_DBUS_ERRORS, TP_DBUS_ERROR_INVALID_BUS_NAME,
337               "Invalid bus name '%s': contains invalid character '%c'",
338               name, *ptr);
339           return FALSE;
340         }
341 
342       last = *ptr;
343     }
344 
345   if (last == '.')
346     {
347       g_set_error (error, TP_DBUS_ERRORS, TP_DBUS_ERROR_INVALID_BUS_NAME,
348           "Invalid bus name '%s': must not end with '.'", name);
349       return FALSE;
350     }
351 
352   if (!dot)
353     {
354       g_set_error (error, TP_DBUS_ERRORS, TP_DBUS_ERROR_INVALID_BUS_NAME,
355           "Invalid bus name '%s': must contain '.'", name);
356       return FALSE;
357     }
358 
359   return TRUE;
360 }
361 
362 /**
363  * tp_dbus_check_valid_interface_name:
364  * @name: a possible interface name
365  * @error: used to raise %TP_DBUS_ERROR_INVALID_INTERFACE_NAME if %FALSE is
366  *  returned
367  *
368  * Check that the given string is a valid D-Bus interface name. This is
369  * also appropriate to use to check for valid error names.
370  *
371  * Since GIO 2.26, g_dbus_is_interface_name() should always return the same
372  * thing, although the GLib function does not raise an error explaining why
373  * the interface name is incorrect.
374  *
375  * Returns: %TRUE if @name is valid
376  *
377  * Since: 0.7.1
378  */
379 gboolean
tp_dbus_check_valid_interface_name(const gchar * name,GError ** error)380 tp_dbus_check_valid_interface_name (const gchar *name,
381                                     GError **error)
382 {
383   gboolean dot = FALSE;
384   gchar last;
385   const gchar *ptr;
386 
387   g_return_val_if_fail (name != NULL, FALSE);
388 
389   if (name[0] == '\0')
390     {
391       g_set_error (error, TP_DBUS_ERRORS, TP_DBUS_ERROR_INVALID_INTERFACE_NAME,
392           "The empty string is not a valid interface name");
393       return FALSE;
394     }
395 
396   if (strlen (name) > 255)
397     {
398       g_set_error (error, TP_DBUS_ERRORS, TP_DBUS_ERROR_INVALID_INTERFACE_NAME,
399           "Invalid interface name: too long (> 255 characters)");
400       return FALSE;
401     }
402 
403   last = '\0';
404 
405   for (ptr = name; *ptr != '\0'; ptr++)
406     {
407       if (*ptr == '.')
408         {
409           dot = TRUE;
410 
411           if (last == '.')
412             {
413               g_set_error (error, TP_DBUS_ERRORS,
414                   TP_DBUS_ERROR_INVALID_INTERFACE_NAME,
415                   "Invalid interface name '%s': contains '..'", name);
416               return FALSE;
417             }
418           else if (last == '\0')
419             {
420               g_set_error (error, TP_DBUS_ERRORS,
421                   TP_DBUS_ERROR_INVALID_INTERFACE_NAME,
422                   "Invalid interface name '%s': must not start with '.'",
423                   name);
424               return FALSE;
425             }
426         }
427       else if (g_ascii_isdigit (*ptr))
428         {
429           if (last == '\0')
430             {
431               g_set_error (error, TP_DBUS_ERRORS,
432                   TP_DBUS_ERROR_INVALID_INTERFACE_NAME,
433                   "Invalid interface name '%s': must not start with a digit",
434                   name);
435               return FALSE;
436             }
437           else if (last == '.')
438             {
439               g_set_error (error, TP_DBUS_ERRORS,
440                   TP_DBUS_ERROR_INVALID_INTERFACE_NAME,
441                   "Invalid interface name '%s': a digit must not follow '.'",
442                   name);
443               return FALSE;
444             }
445         }
446       else if (!g_ascii_isalpha (*ptr) && *ptr != '_')
447         {
448           g_set_error (error, TP_DBUS_ERRORS,
449               TP_DBUS_ERROR_INVALID_INTERFACE_NAME,
450               "Invalid interface name '%s': contains invalid character '%c'",
451               name, *ptr);
452           return FALSE;
453         }
454 
455       last = *ptr;
456     }
457 
458   if (last == '.')
459     {
460       g_set_error (error, TP_DBUS_ERRORS, TP_DBUS_ERROR_INVALID_INTERFACE_NAME,
461           "Invalid interface name '%s': must not end with '.'", name);
462       return FALSE;
463     }
464 
465   if (!dot)
466     {
467       g_set_error (error, TP_DBUS_ERRORS, TP_DBUS_ERROR_INVALID_INTERFACE_NAME,
468           "Invalid interface name '%s': must contain '.'", name);
469       return FALSE;
470     }
471 
472   return TRUE;
473 }
474 
475 /**
476  * tp_dbus_check_valid_member_name:
477  * @name: a possible member name
478  * @error: used to raise %TP_DBUS_ERROR_INVALID_MEMBER_NAME if %FALSE is
479  *  returned
480  *
481  * Check that the given string is a valid D-Bus member (method or signal) name.
482  *
483  * Since GIO 2.26, g_dbus_is_member_name() should always return the same
484  * thing, although the GLib function does not raise an error explaining why
485  * the interface name is incorrect.
486  *
487  * Returns: %TRUE if @name is valid
488  *
489  * Since: 0.7.1
490  */
491 gboolean
tp_dbus_check_valid_member_name(const gchar * name,GError ** error)492 tp_dbus_check_valid_member_name (const gchar *name,
493                                  GError **error)
494 {
495   const gchar *ptr;
496 
497   g_return_val_if_fail (name != NULL, FALSE);
498 
499   if (name[0] == '\0')
500     {
501       g_set_error (error, TP_DBUS_ERRORS, TP_DBUS_ERROR_INVALID_MEMBER_NAME,
502           "The empty string is not a valid method or signal name");
503       return FALSE;
504     }
505 
506   if (strlen (name) > 255)
507     {
508       g_set_error (error, TP_DBUS_ERRORS, TP_DBUS_ERROR_INVALID_MEMBER_NAME,
509           "Invalid method or signal name: too long (> 255 characters)");
510       return FALSE;
511     }
512 
513   for (ptr = name; *ptr != '\0'; ptr++)
514     {
515       if (g_ascii_isdigit (*ptr))
516         {
517           if (ptr == name)
518             {
519               g_set_error (error, TP_DBUS_ERRORS,
520                   TP_DBUS_ERROR_INVALID_MEMBER_NAME,
521                   "Invalid method or signal name '%s': must not start with "
522                   "a digit", name);
523               return FALSE;
524             }
525         }
526       else if (!g_ascii_isalpha (*ptr) && *ptr != '_')
527         {
528           g_set_error (error, TP_DBUS_ERRORS,
529               TP_DBUS_ERROR_INVALID_MEMBER_NAME,
530               "Invalid method or signal name '%s': contains invalid "
531               "character '%c'",
532               name, *ptr);
533           return FALSE;
534         }
535     }
536 
537   return TRUE;
538 }
539 
540 /**
541  * tp_dbus_check_valid_object_path:
542  * @path: a possible object path
543  * @error: used to raise %TP_DBUS_ERROR_INVALID_OBJECT_PATH if %FALSE is
544  *  returned
545  *
546  * Check that the given string is a valid D-Bus object path. Since GLib 2.24,
547  * g_variant_is_object_path() should always return the same thing as this
548  * function, although it doesn't provide an error explaining why the object
549  * path is invalid.
550  *
551  * Returns: %TRUE if @path is valid
552  *
553  * Since: 0.7.1
554  */
555 gboolean
tp_dbus_check_valid_object_path(const gchar * path,GError ** error)556 tp_dbus_check_valid_object_path (const gchar *path, GError **error)
557 {
558   const gchar *ptr;
559 
560   g_return_val_if_fail (path != NULL, FALSE);
561 
562   if (path[0] != '/')
563     {
564       g_set_error (error, TP_DBUS_ERRORS, TP_DBUS_ERROR_INVALID_OBJECT_PATH,
565           "Invalid object path '%s': must start with '/'",
566           path);
567       return FALSE;
568     }
569 
570   if (path[1] == '\0')
571     return TRUE;
572 
573   for (ptr = path + 1; *ptr != '\0'; ptr++)
574     {
575       if (*ptr == '/')
576         {
577           if (ptr[-1] == '/')
578             {
579               g_set_error (error, TP_DBUS_ERRORS,
580                   TP_DBUS_ERROR_INVALID_OBJECT_PATH,
581                   "Invalid object path '%s': contains '//'", path);
582               return FALSE;
583             }
584         }
585       else if (!g_ascii_isalnum (*ptr) && *ptr != '_')
586         {
587           g_set_error (error, TP_DBUS_ERRORS,
588               TP_DBUS_ERROR_INVALID_OBJECT_PATH,
589               "Invalid object path '%s': contains invalid character '%c'",
590               path, *ptr);
591           return FALSE;
592         }
593     }
594 
595   if (ptr[-1] == '/')
596     {
597         g_set_error (error, TP_DBUS_ERRORS, TP_DBUS_ERROR_INVALID_OBJECT_PATH,
598             "Invalid object path '%s': is not '/' but does end with '/'",
599             path);
600         return FALSE;
601     }
602 
603   return TRUE;
604 }
605 
606 /**
607  * tp_g_value_slice_new_bytes: (skip)
608  * @length: number of bytes to copy
609  * @bytes: location of an array of bytes to be copied (this may be %NULL
610  *  if and only if length is 0)
611  *
612  * Slice-allocate a #GValue containing a byte-array, using
613  * tp_g_value_slice_new_boxed(). This function is convenient to use when
614  * constructing hash tables from string to #GValue, for example.
615  *
616  * Returns: a #GValue of type %DBUS_TYPE_G_UCHAR_ARRAY whose value is a copy
617  * of @length bytes from @bytes, to be freed with tp_g_value_slice_free() or
618  * g_slice_free()
619  *
620  * Since: 0.7.27
621  */
622 GValue *
tp_g_value_slice_new_bytes(guint length,gconstpointer bytes)623 tp_g_value_slice_new_bytes (guint length,
624                             gconstpointer bytes)
625 {
626   GArray *arr;
627 
628   g_return_val_if_fail (length == 0 || bytes != NULL, NULL);
629   arr = g_array_sized_new (FALSE, FALSE, 1, length);
630 
631   if (length > 0)
632     g_array_append_vals (arr, bytes, length);
633 
634   return tp_g_value_slice_new_take_boxed (DBUS_TYPE_G_UCHAR_ARRAY, arr);
635 }
636 
637 /**
638  * tp_g_value_slice_new_take_bytes: (skip)
639  * @bytes: a non-NULL #GArray of guchar, ownership of which will be taken by
640  *  the #GValue
641  *
642  * Slice-allocate a #GValue containing @bytes, using
643  * tp_g_value_slice_new_boxed(). This function is convenient to use when
644  * constructing hash tables from string to #GValue, for example.
645  *
646  * Returns: a #GValue of type %DBUS_TYPE_G_UCHAR_ARRAY whose value is
647  * @bytes, to be freed with tp_g_value_slice_free() or
648  * g_slice_free()
649  *
650  * Since: 0.7.27
651  */
652 GValue *
tp_g_value_slice_new_take_bytes(GArray * bytes)653 tp_g_value_slice_new_take_bytes (GArray *bytes)
654 {
655   g_return_val_if_fail (bytes != NULL, NULL);
656   return tp_g_value_slice_new_take_boxed (DBUS_TYPE_G_UCHAR_ARRAY, bytes);
657 }
658 
659 /**
660  * tp_g_value_slice_new_object_path: (skip)
661  * @path: a valid D-Bus object path which will be copied
662  *
663  * Slice-allocate a #GValue containing an object path, using
664  * tp_g_value_slice_new_boxed(). This function is convenient to use when
665  * constructing hash tables from string to #GValue, for example.
666  *
667  * Returns: a #GValue of type %DBUS_TYPE_G_OBJECT_PATH whose value is a copy
668  * of @path, to be freed with tp_g_value_slice_free() or g_slice_free()
669  *
670  * Since: 0.7.27
671  */
672 GValue *
tp_g_value_slice_new_object_path(const gchar * path)673 tp_g_value_slice_new_object_path (const gchar *path)
674 {
675   g_return_val_if_fail (tp_dbus_check_valid_object_path (path, NULL), NULL);
676   return tp_g_value_slice_new_boxed (DBUS_TYPE_G_OBJECT_PATH, path);
677 }
678 
679 /**
680  * tp_g_value_slice_new_static_object_path: (skip)
681  * @path: a valid D-Bus object path which must remain valid forever
682  *
683  * Slice-allocate a #GValue containing an object path, using
684  * tp_g_value_slice_new_static_boxed(). This function is convenient to use when
685  * constructing hash tables from string to #GValue, for example.
686  *
687  * Returns: a #GValue of type %DBUS_TYPE_G_OBJECT_PATH whose value is @path,
688  * to be freed with tp_g_value_slice_free() or g_slice_free()
689  *
690  * Since: 0.7.27
691  */
692 GValue *
tp_g_value_slice_new_static_object_path(const gchar * path)693 tp_g_value_slice_new_static_object_path (const gchar *path)
694 {
695   g_return_val_if_fail (tp_dbus_check_valid_object_path (path, NULL), NULL);
696   return tp_g_value_slice_new_static_boxed (DBUS_TYPE_G_OBJECT_PATH, path);
697 }
698 
699 /**
700  * tp_g_value_slice_new_take_object_path: (skip)
701  * @path: a valid D-Bus object path which will be freed with g_free() by the
702  *  returned #GValue (the caller must own it before calling this function, but
703  *  no longer owns it after this function returns)
704  *
705  * Slice-allocate a #GValue containing an object path, using
706  * tp_g_value_slice_new_take_boxed(). This function is convenient to use when
707  * constructing hash tables from string to #GValue, for example.
708  *
709  * Returns: a #GValue of type %DBUS_TYPE_G_OBJECT_PATH whose value is @path,
710  * to be freed with tp_g_value_slice_free() or g_slice_free()
711  *
712  * Since: 0.7.27
713  */
714 GValue *
tp_g_value_slice_new_take_object_path(gchar * path)715 tp_g_value_slice_new_take_object_path (gchar *path)
716 {
717   g_return_val_if_fail (tp_dbus_check_valid_object_path (path, NULL), NULL);
718   return tp_g_value_slice_new_take_boxed (DBUS_TYPE_G_OBJECT_PATH, path);
719 }
720 
721 /**
722  * tp_asv_new: (skip)
723  * @first_key: the name of the first key (or NULL)
724  * @...: type and value for the first key, followed by a NULL-terminated list
725  *  of (key, type, value) tuples
726  *
727  * Creates a new #GHashTable for use with a{sv} maps, containing the values
728  * passed in as parameters.
729  *
730  * The #GHashTable is synonymous with:
731  * <informalexample><programlisting>
732  * GHashTable *asv = g_hash_table_new_full (g_str_hash, g_str_equal,
733  *    NULL, (GDestroyNotify) tp_g_value_slice_free);
734  * </programlisting></informalexample>
735  * Followed by manual insertion of each of the parameters.
736  *
737  * Parameters are stored in slice-allocated GValues and should be set using
738  * tp_asv_set_*() and retrieved using tp_asv_get_*().
739  *
740  * tp_g_value_slice_new() and tp_g_value_slice_dup() may also be used to insert
741  * into the map if required.
742  * <informalexample><programlisting>
743  * g_hash_table_insert (parameters, "account",
744  *    tp_g_value_slice_new_string ("bob@mcbadgers.com"));
745  * </programlisting></informalexample>
746  *
747  * <example>
748  *  <title>Using tp_asv_new()</title>
749  *  <programlisting>
750  * GHashTable *parameters = tp_asv_new (
751  *    "answer", G_TYPE_INT, 42,
752  *    "question", G_TYPE_STRING, "We just don't know",
753  *    NULL);</programlisting>
754  * </example>
755  *
756  * Allocated values will be automatically free'd when overwritten, removed or
757  * the hash table destroyed with g_hash_table_unref().
758  *
759  * Returns: a newly created #GHashTable for storing a{sv} maps, free with
760  * g_hash_table_unref().
761  * Since: 0.7.29
762  */
763 GHashTable *
tp_asv_new(const gchar * first_key,...)764 tp_asv_new (const gchar *first_key, ...)
765 {
766   va_list var_args;
767   char *key;
768   GType type;
769   GValue *value;
770   char *error = NULL; /* NB: not a GError! */
771 
772   /* create a GHashTable */
773   GHashTable *asv = g_hash_table_new_full (g_str_hash, g_str_equal,
774       NULL, (GDestroyNotify) tp_g_value_slice_free);
775 
776   va_start (var_args, first_key);
777 
778   for (key = (char *) first_key; key != NULL; key = va_arg (var_args, char *))
779   {
780     type = va_arg (var_args, GType);
781 
782     value = tp_g_value_slice_new (type);
783     G_VALUE_COLLECT (value, var_args, 0, &error);
784 
785     if (error != NULL)
786     {
787       CRITICAL ("key %s: %s", key, error);
788       g_free (error);
789       error = NULL;
790       tp_g_value_slice_free (value);
791       continue;
792     }
793 
794     g_hash_table_insert (asv, key, value);
795   }
796 
797   va_end (var_args);
798 
799   return asv;
800 }
801 
802 /**
803  * tp_asv_get_boolean:
804  * @asv: (element-type utf8 GObject.Value): A GHashTable where the keys are
805  * strings and the values are GValues
806  * @key: The key to look up
807  * @valid: (out): Either %NULL, or a location to store %TRUE if the key actually
808  *  exists and has a boolean value
809  *
810  * If a value for @key in @asv is present and boolean, return it,
811  * and set *@valid to %TRUE if @valid is not %NULL.
812  *
813  * Otherwise return %FALSE, and set *@valid to %FALSE if @valid is not %NULL.
814  *
815  * Returns: a boolean value for @key
816  * Since: 0.7.9
817  */
818 gboolean
tp_asv_get_boolean(const GHashTable * asv,const gchar * key,gboolean * valid)819 tp_asv_get_boolean (const GHashTable *asv,
820                     const gchar *key,
821                     gboolean *valid)
822 {
823   GValue *value;
824 
825   g_return_val_if_fail (asv != NULL, FALSE);
826   g_return_val_if_fail (key != NULL, FALSE);
827 
828   value = g_hash_table_lookup ((GHashTable *) asv, key);
829 
830   if (value == NULL || !G_VALUE_HOLDS_BOOLEAN (value))
831     {
832       if (valid != NULL)
833         *valid = FALSE;
834 
835       return FALSE;
836     }
837 
838   if (valid != NULL)
839     *valid = TRUE;
840 
841   return g_value_get_boolean (value);
842 }
843 
844 /**
845  * tp_asv_set_boolean: (skip)
846  * @asv: a #GHashTable created with tp_asv_new()
847  * @key: string key
848  * @value: value
849  *
850  * Stores the value in the map.
851  *
852  * The value is stored as a slice-allocated GValue.
853  *
854  * See Also: tp_asv_new(), tp_asv_get_boolean(), tp_g_value_slice_new_boolean()
855  * Since: 0.7.29
856  */
857 void
tp_asv_set_boolean(GHashTable * asv,const gchar * key,gboolean value)858 tp_asv_set_boolean (GHashTable *asv,
859                     const gchar *key,
860                     gboolean value)
861 {
862   g_return_if_fail (asv != NULL);
863   g_return_if_fail (key != NULL);
864 
865   g_hash_table_insert (asv, (char *) key, tp_g_value_slice_new_boolean (value));
866 }
867 
868 /**
869  * tp_asv_get_bytes:
870  * @asv: (element-type utf8 GObject.Value): A GHashTable where the keys are
871  * strings and the values are GValues
872  * @key: The key to look up
873  *
874  * If a value for @key in @asv is present and is an array of bytes
875  * (its GType is %DBUS_TYPE_G_UCHAR_ARRAY), return it.
876  *
877  * Otherwise return %NULL.
878  *
879  * The returned value is not copied, and is only valid as long as the value
880  * for @key in @asv is not removed or altered. Copy it with
881  * g_boxed_copy (DBUS_TYPE_G_UCHAR_ARRAY, ...) if you need to keep
882  * it for longer.
883  *
884  * Returns: (transfer none) (allow-none) (element-type guint8): the string value
885  * of @key, or %NULL
886  * Since: 0.7.9
887  */
888 const GArray *
tp_asv_get_bytes(const GHashTable * asv,const gchar * key)889 tp_asv_get_bytes (const GHashTable *asv,
890                    const gchar *key)
891 {
892   GValue *value;
893 
894   g_return_val_if_fail (asv != NULL, NULL);
895   g_return_val_if_fail (key != NULL, NULL);
896 
897   value = g_hash_table_lookup ((GHashTable *) asv, key);
898 
899   if (value == NULL || !G_VALUE_HOLDS (value, DBUS_TYPE_G_UCHAR_ARRAY))
900     return NULL;
901 
902   return g_value_get_boxed (value);
903 }
904 
905 /**
906  * tp_asv_set_bytes: (skip)
907  * @asv: a #GHashTable created with tp_asv_new()
908  * @key: string key
909  * @length: the number of bytes to copy
910  * @bytes: location of an array of bytes to be copied (this may be %NULL
911  * if and only if length is 0)
912  *
913  * Stores the value in the map.
914  *
915  * The value is stored as a slice-allocated GValue.
916  *
917  * See Also: tp_asv_new(), tp_asv_get_bytes(), tp_g_value_slice_new_bytes()
918  * Since: 0.7.29
919  */
920 void
tp_asv_set_bytes(GHashTable * asv,const gchar * key,guint length,gconstpointer bytes)921 tp_asv_set_bytes (GHashTable *asv,
922                   const gchar *key,
923                   guint length,
924                   gconstpointer bytes)
925 {
926   g_return_if_fail (asv != NULL);
927   g_return_if_fail (key != NULL);
928   g_return_if_fail (!(length > 0 && bytes == NULL));
929 
930   g_hash_table_insert (asv, (char *) key,
931       tp_g_value_slice_new_bytes (length, bytes));
932 }
933 
934 /**
935  * tp_asv_take_bytes: (skip)
936  * @asv: a #GHashTable created with tp_asv_new()
937  * @key: string key
938  * @value: a non-NULL #GArray of %guchar, ownership of which will be taken by
939  * the #GValue
940  *
941  * Stores the value in the map.
942  *
943  * The value is stored as a slice-allocated GValue.
944  *
945  * See Also: tp_asv_new(), tp_asv_get_bytes(), tp_g_value_slice_new_take_bytes()
946  * Since: 0.7.29
947  */
948 void
tp_asv_take_bytes(GHashTable * asv,const gchar * key,GArray * value)949 tp_asv_take_bytes (GHashTable *asv,
950                    const gchar *key,
951                    GArray *value)
952 {
953   g_return_if_fail (asv != NULL);
954   g_return_if_fail (key != NULL);
955   g_return_if_fail (value != NULL);
956 
957   g_hash_table_insert (asv, (char *) key,
958       tp_g_value_slice_new_take_bytes (value));
959 }
960 
961 /**
962  * tp_asv_get_string:
963  * @asv: (element-type utf8 GObject.Value): A GHashTable where the keys are
964  * strings and the values are GValues
965  * @key: The key to look up
966  *
967  * If a value for @key in @asv is present and is a string, return it.
968  *
969  * Otherwise return %NULL.
970  *
971  * The returned value is not copied, and is only valid as long as the value
972  * for @key in @asv is not removed or altered. Copy it with g_strdup() if you
973  * need to keep it for longer.
974  *
975  * Returns: (transfer none) (allow-none): the string value of @key, or %NULL
976  * Since: 0.7.9
977  */
978 const gchar *
tp_asv_get_string(const GHashTable * asv,const gchar * key)979 tp_asv_get_string (const GHashTable *asv,
980                    const gchar *key)
981 {
982   GValue *value;
983 
984   g_return_val_if_fail (asv != NULL, NULL);
985   g_return_val_if_fail (key != NULL, NULL);
986 
987   value = g_hash_table_lookup ((GHashTable *) asv, key);
988 
989   if (value == NULL || !G_VALUE_HOLDS_STRING (value))
990     return NULL;
991 
992   return g_value_get_string (value);
993 }
994 
995 /**
996  * tp_asv_set_string: (skip)
997  * @asv: a #GHashTable created with tp_asv_new()
998  * @key: string key
999  * @value: value
1000  *
1001  * Stores the value in the map.
1002  *
1003  * The value is stored as a slice-allocated GValue.
1004  *
1005  * See Also: tp_asv_new(), tp_asv_get_string(), tp_g_value_slice_new_string()
1006  * Since: 0.7.29
1007  */
1008 void
tp_asv_set_string(GHashTable * asv,const gchar * key,const gchar * value)1009 tp_asv_set_string (GHashTable *asv,
1010                    const gchar *key,
1011                    const gchar *value)
1012 {
1013   g_return_if_fail (asv != NULL);
1014   g_return_if_fail (key != NULL);
1015 
1016   g_hash_table_insert (asv, (char *) key, tp_g_value_slice_new_string (value));
1017 }
1018 
1019 /**
1020  * tp_asv_take_string: (skip)
1021  * @asv: a #GHashTable created with tp_asv_new()
1022  * @key: string key
1023  * @value: value
1024  *
1025  * Stores the value in the map.
1026  *
1027  * The value is stored as a slice-allocated GValue.
1028  *
1029  * See Also: tp_asv_new(), tp_asv_get_string(),
1030  * tp_g_value_slice_new_take_string()
1031  * Since: 0.7.29
1032  */
1033 void
tp_asv_take_string(GHashTable * asv,const gchar * key,gchar * value)1034 tp_asv_take_string (GHashTable *asv,
1035                     const gchar *key,
1036                     gchar *value)
1037 {
1038   g_return_if_fail (asv != NULL);
1039   g_return_if_fail (key != NULL);
1040 
1041   g_hash_table_insert (asv, (char *) key,
1042       tp_g_value_slice_new_take_string (value));
1043 }
1044 
1045 /**
1046  * tp_asv_set_static_string: (skip)
1047  * @asv: a #GHashTable created with tp_asv_new()
1048  * @key: string key
1049  * @value: value
1050  *
1051  * Stores the value in the map.
1052  *
1053  * The value is stored as a slice-allocated GValue.
1054  *
1055  * See Also: tp_asv_new(), tp_asv_get_string(),
1056  * tp_g_value_slice_new_static_string()
1057  * Since: 0.7.29
1058  */
1059 void
tp_asv_set_static_string(GHashTable * asv,const gchar * key,const gchar * value)1060 tp_asv_set_static_string (GHashTable *asv,
1061                           const gchar *key,
1062                           const gchar *value)
1063 {
1064   g_return_if_fail (asv != NULL);
1065   g_return_if_fail (key != NULL);
1066 
1067   g_hash_table_insert (asv, (char *) key,
1068       tp_g_value_slice_new_static_string (value));
1069 }
1070 
1071 /**
1072  * tp_asv_get_int32:
1073  * @asv: (element-type utf8 GObject.Value): A GHashTable where the keys are
1074  * strings and the values are GValues
1075  * @key: The key to look up
1076  * @valid: (out): Either %NULL, or a location in which to store %TRUE on success
1077  * or %FALSE on failure
1078  *
1079  * If a value for @key in @asv is present, has an integer type used by
1080  * dbus-glib (guchar, gint, guint, gint64 or guint64) and fits in the
1081  * range of a gint32, return it, and if @valid is not %NULL, set *@valid to
1082  * %TRUE.
1083  *
1084  * Otherwise, return 0, and if @valid is not %NULL, set *@valid to %FALSE.
1085  *
1086  * Returns: the 32-bit signed integer value of @key, or 0
1087  * Since: 0.7.9
1088  */
1089 gint32
tp_asv_get_int32(const GHashTable * asv,const gchar * key,gboolean * valid)1090 tp_asv_get_int32 (const GHashTable *asv,
1091                   const gchar *key,
1092                   gboolean *valid)
1093 {
1094   gint64 i;
1095   guint64 u;
1096   gint32 ret;
1097   GValue *value;
1098 
1099   g_return_val_if_fail (asv != NULL, 0);
1100   g_return_val_if_fail (key != NULL, 0);
1101 
1102   value = g_hash_table_lookup ((GHashTable *) asv, key);
1103 
1104   if (value == NULL)
1105     goto return_invalid;
1106 
1107   switch (G_VALUE_TYPE (value))
1108     {
1109     case G_TYPE_UCHAR:
1110       ret = g_value_get_uchar (value);
1111       break;
1112 
1113     case G_TYPE_UINT:
1114       u = g_value_get_uint (value);
1115 
1116       if (G_UNLIKELY (u > G_MAXINT32))
1117         goto return_invalid;
1118 
1119       ret = u;
1120       break;
1121 
1122     case G_TYPE_INT:
1123       ret = g_value_get_int (value);
1124       break;
1125 
1126     case G_TYPE_INT64:
1127       i = g_value_get_int64 (value);
1128 
1129       if (G_UNLIKELY (i < G_MININT32 || i > G_MAXINT32))
1130         goto return_invalid;
1131 
1132       ret = i;
1133       break;
1134 
1135     case G_TYPE_UINT64:
1136       u = g_value_get_uint64 (value);
1137 
1138       if (G_UNLIKELY (u > G_MAXINT32))
1139         goto return_invalid;
1140 
1141       ret = u;
1142       break;
1143 
1144     default:
1145       goto return_invalid;
1146     }
1147 
1148   if (valid != NULL)
1149     *valid = TRUE;
1150 
1151   return ret;
1152 
1153 return_invalid:
1154   if (valid != NULL)
1155     *valid = FALSE;
1156 
1157   return 0;
1158 }
1159 
1160 /**
1161  * tp_asv_set_int32: (skip)
1162  * @asv: a #GHashTable created with tp_asv_new()
1163  * @key: string key
1164  * @value: value
1165  *
1166  * Stores the value in the map.
1167  *
1168  * The value is stored as a slice-allocated GValue.
1169  *
1170  * See Also: tp_asv_new(), tp_asv_get_int32(), tp_g_value_slice_new_int()
1171  * Since: 0.7.29
1172  */
1173 void
tp_asv_set_int32(GHashTable * asv,const gchar * key,gint32 value)1174 tp_asv_set_int32 (GHashTable *asv,
1175                   const gchar *key,
1176                   gint32 value)
1177 {
1178   g_return_if_fail (asv != NULL);
1179   g_return_if_fail (key != NULL);
1180 
1181   g_hash_table_insert (asv, (char *) key, tp_g_value_slice_new_int (value));
1182 }
1183 
1184 /**
1185  * tp_asv_get_uint32:
1186  * @asv: (element-type utf8 GObject.Value): A GHashTable where the keys are
1187  * strings and the values are GValues
1188  * @key: The key to look up
1189  * @valid: (out): Either %NULL, or a location in which to store %TRUE on success
1190  * or %FALSE on failure
1191  *
1192  * If a value for @key in @asv is present, has an integer type used by
1193  * dbus-glib (guchar, gint, guint, gint64 or guint64) and fits in the
1194  * range of a guint32, return it, and if @valid is not %NULL, set *@valid to
1195  * %TRUE.
1196  *
1197  * Otherwise, return 0, and if @valid is not %NULL, set *@valid to %FALSE.
1198  *
1199  * Returns: the 32-bit unsigned integer value of @key, or 0
1200  * Since: 0.7.9
1201  */
1202 guint32
tp_asv_get_uint32(const GHashTable * asv,const gchar * key,gboolean * valid)1203 tp_asv_get_uint32 (const GHashTable *asv,
1204                    const gchar *key,
1205                    gboolean *valid)
1206 {
1207   gint64 i;
1208   guint64 u;
1209   guint32 ret;
1210   GValue *value;
1211 
1212   g_return_val_if_fail (asv != NULL, 0);
1213   g_return_val_if_fail (key != NULL, 0);
1214 
1215   value = g_hash_table_lookup ((GHashTable *) asv, key);
1216 
1217   if (value == NULL)
1218     goto return_invalid;
1219 
1220   switch (G_VALUE_TYPE (value))
1221     {
1222     case G_TYPE_UCHAR:
1223       ret = g_value_get_uchar (value);
1224       break;
1225 
1226     case G_TYPE_UINT:
1227       ret = g_value_get_uint (value);
1228       break;
1229 
1230     case G_TYPE_INT:
1231       i = g_value_get_int (value);
1232 
1233       if (G_UNLIKELY (i < 0))
1234         goto return_invalid;
1235 
1236       ret = i;
1237       break;
1238 
1239     case G_TYPE_INT64:
1240       i = g_value_get_int64 (value);
1241 
1242       if (G_UNLIKELY (i < 0 || i > G_MAXUINT32))
1243         goto return_invalid;
1244 
1245       ret = i;
1246       break;
1247 
1248     case G_TYPE_UINT64:
1249       u = g_value_get_uint64 (value);
1250 
1251       if (G_UNLIKELY (u > G_MAXUINT32))
1252         goto return_invalid;
1253 
1254       ret = u;
1255       break;
1256 
1257     default:
1258       goto return_invalid;
1259     }
1260 
1261   if (valid != NULL)
1262     *valid = TRUE;
1263 
1264   return ret;
1265 
1266 return_invalid:
1267   if (valid != NULL)
1268     *valid = FALSE;
1269 
1270   return 0;
1271 }
1272 
1273 /**
1274  * tp_asv_set_uint32: (skip)
1275  * @asv: a #GHashTable created with tp_asv_new()
1276  * @key: string key
1277  * @value: value
1278  *
1279  * Stores the value in the map.
1280  *
1281  * The value is stored as a slice-allocated GValue.
1282  *
1283  * See Also: tp_asv_new(), tp_asv_get_uint32(), tp_g_value_slice_new_uint()
1284  * Since: 0.7.29
1285  */
1286 void
tp_asv_set_uint32(GHashTable * asv,const gchar * key,guint32 value)1287 tp_asv_set_uint32 (GHashTable *asv,
1288                    const gchar *key,
1289                    guint32 value)
1290 {
1291   g_return_if_fail (asv != NULL);
1292   g_return_if_fail (key != NULL);
1293 
1294   g_hash_table_insert (asv, (char *) key, tp_g_value_slice_new_uint (value));
1295 }
1296 
1297 /**
1298  * tp_asv_get_int64:
1299  * @asv: (element-type utf8 GObject.Value): A GHashTable where the keys are
1300  * strings and the values are GValues
1301  * @key: The key to look up
1302  * @valid: (out): Either %NULL, or a location in which to store %TRUE on success
1303  * or %FALSE on failure
1304  *
1305  * If a value for @key in @asv is present, has an integer type used by
1306  * dbus-glib (guchar, gint, guint, gint64 or guint64) and fits in the
1307  * range of a gint64, return it, and if @valid is not %NULL, set *@valid to
1308  * %TRUE.
1309  *
1310  * Otherwise, return 0, and if @valid is not %NULL, set *@valid to %FALSE.
1311  *
1312  * Returns: the 64-bit signed integer value of @key, or 0
1313  * Since: 0.7.9
1314  */
1315 gint64
tp_asv_get_int64(const GHashTable * asv,const gchar * key,gboolean * valid)1316 tp_asv_get_int64 (const GHashTable *asv,
1317                   const gchar *key,
1318                   gboolean *valid)
1319 {
1320   gint64 ret;
1321   guint64 u;
1322   GValue *value;
1323 
1324   g_return_val_if_fail (asv != NULL, 0);
1325   g_return_val_if_fail (key != NULL, 0);
1326 
1327   value = g_hash_table_lookup ((GHashTable *) asv, key);
1328 
1329   if (value == NULL)
1330     goto return_invalid;
1331 
1332   switch (G_VALUE_TYPE (value))
1333     {
1334     case G_TYPE_UCHAR:
1335       ret = g_value_get_uchar (value);
1336       break;
1337 
1338     case G_TYPE_UINT:
1339       ret = g_value_get_uint (value);
1340       break;
1341 
1342     case G_TYPE_INT:
1343       ret = g_value_get_int (value);
1344       break;
1345 
1346     case G_TYPE_INT64:
1347       ret = g_value_get_int64 (value);
1348       break;
1349 
1350     case G_TYPE_UINT64:
1351       u = g_value_get_uint64 (value);
1352 
1353       if (G_UNLIKELY (u > G_MAXINT64))
1354         goto return_invalid;
1355 
1356       ret = u;
1357       break;
1358 
1359     default:
1360       goto return_invalid;
1361     }
1362 
1363   if (valid != NULL)
1364     *valid = TRUE;
1365 
1366   return ret;
1367 
1368 return_invalid:
1369   if (valid != NULL)
1370     *valid = FALSE;
1371 
1372   return 0;
1373 }
1374 
1375 /**
1376  * tp_asv_set_int64: (skip)
1377  * @asv: a #GHashTable created with tp_asv_new()
1378  * @key: string key
1379  * @value: value
1380  *
1381  * Stores the value in the map.
1382  *
1383  * The value is stored as a slice-allocated GValue.
1384  *
1385  * See Also: tp_asv_new(), tp_asv_get_int64(), tp_g_value_slice_new_int64()
1386  * Since: 0.7.29
1387  */
1388 void
tp_asv_set_int64(GHashTable * asv,const gchar * key,gint64 value)1389 tp_asv_set_int64 (GHashTable *asv,
1390                   const gchar *key,
1391                   gint64 value)
1392 {
1393   g_return_if_fail (asv != NULL);
1394   g_return_if_fail (key != NULL);
1395 
1396   g_hash_table_insert (asv, (char *) key, tp_g_value_slice_new_int64 (value));
1397 }
1398 
1399 /**
1400  * tp_asv_get_uint64:
1401  * @asv: (element-type utf8 GObject.Value): A GHashTable where the keys are
1402  * strings and the values are GValues
1403  * @key: The key to look up
1404  * @valid: (out): Either %NULL, or a location in which to store %TRUE on success
1405  * or %FALSE on failure
1406  *
1407  * If a value for @key in @asv is present, has an integer type used by
1408  * dbus-glib (guchar, gint, guint, gint64 or guint64) and is non-negative,
1409  * return it, and if @valid is not %NULL, set *@valid to %TRUE.
1410  *
1411  * Otherwise, return 0, and if @valid is not %NULL, set *@valid to %FALSE.
1412  *
1413  * Returns: the 64-bit unsigned integer value of @key, or 0
1414  * Since: 0.7.9
1415  */
1416 guint64
tp_asv_get_uint64(const GHashTable * asv,const gchar * key,gboolean * valid)1417 tp_asv_get_uint64 (const GHashTable *asv,
1418                    const gchar *key,
1419                    gboolean *valid)
1420 {
1421   gint64 tmp;
1422   guint64 ret;
1423   GValue *value;
1424 
1425   g_return_val_if_fail (asv != NULL, 0);
1426   g_return_val_if_fail (key != NULL, 0);
1427 
1428   value = g_hash_table_lookup ((GHashTable *) asv, key);
1429 
1430   if (value == NULL)
1431     goto return_invalid;
1432 
1433   switch (G_VALUE_TYPE (value))
1434     {
1435     case G_TYPE_UCHAR:
1436       ret = g_value_get_uchar (value);
1437       break;
1438 
1439     case G_TYPE_UINT:
1440       ret = g_value_get_uint (value);
1441       break;
1442 
1443     case G_TYPE_INT:
1444       tmp = g_value_get_int (value);
1445 
1446       if (G_UNLIKELY (tmp < 0))
1447         goto return_invalid;
1448 
1449       ret = tmp;
1450       break;
1451 
1452     case G_TYPE_INT64:
1453       tmp = g_value_get_int64 (value);
1454 
1455       if (G_UNLIKELY (tmp < 0))
1456         goto return_invalid;
1457 
1458       ret = tmp;
1459       break;
1460 
1461     case G_TYPE_UINT64:
1462       ret = g_value_get_uint64 (value);
1463       break;
1464 
1465     default:
1466       goto return_invalid;
1467     }
1468 
1469   if (valid != NULL)
1470     *valid = TRUE;
1471 
1472   return ret;
1473 
1474 return_invalid:
1475   if (valid != NULL)
1476     *valid = FALSE;
1477 
1478   return 0;
1479 }
1480 
1481 /**
1482  * tp_asv_set_uint64: (skip)
1483  * @asv: a #GHashTable created with tp_asv_new()
1484  * @key: string key
1485  * @value: value
1486  *
1487  * Stores the value in the map.
1488  *
1489  * The value is stored as a slice-allocated GValue.
1490  *
1491  * See Also: tp_asv_new(), tp_asv_get_uint64(), tp_g_value_slice_new_uint64()
1492  * Since: 0.7.29
1493  */
1494 void
tp_asv_set_uint64(GHashTable * asv,const gchar * key,guint64 value)1495 tp_asv_set_uint64 (GHashTable *asv,
1496                    const gchar *key,
1497                    guint64 value)
1498 {
1499   g_return_if_fail (asv != NULL);
1500   g_return_if_fail (key != NULL);
1501 
1502   g_hash_table_insert (asv, (char *) key, tp_g_value_slice_new_uint64 (value));
1503 }
1504 
1505 /**
1506  * tp_asv_get_double:
1507  * @asv: (element-type utf8 GObject.Value): A GHashTable where the keys are
1508  * strings and the values are GValues
1509  * @key: The key to look up
1510  * @valid: (out): Either %NULL, or a location in which to store %TRUE on success
1511  * or %FALSE on failure
1512  *
1513  * If a value for @key in @asv is present and has any numeric type used by
1514  * dbus-glib (guchar, gint, guint, gint64, guint64 or gdouble),
1515  * return it as a double, and if @valid is not %NULL, set *@valid to %TRUE.
1516  *
1517  * Otherwise, return 0.0, and if @valid is not %NULL, set *@valid to %FALSE.
1518  *
1519  * Returns: the double precision floating-point value of @key, or 0.0
1520  * Since: 0.7.9
1521  */
1522 gdouble
tp_asv_get_double(const GHashTable * asv,const gchar * key,gboolean * valid)1523 tp_asv_get_double (const GHashTable *asv,
1524                    const gchar *key,
1525                    gboolean *valid)
1526 {
1527   gdouble ret;
1528   GValue *value;
1529 
1530   g_return_val_if_fail (asv != NULL, 0.0);
1531   g_return_val_if_fail (key != NULL, 0.0);
1532 
1533   value = g_hash_table_lookup ((GHashTable *) asv, key);
1534 
1535   if (value == NULL)
1536     goto return_invalid;
1537 
1538   switch (G_VALUE_TYPE (value))
1539     {
1540     case G_TYPE_DOUBLE:
1541       ret = g_value_get_double (value);
1542       break;
1543 
1544     case G_TYPE_UCHAR:
1545       ret = g_value_get_uchar (value);
1546       break;
1547 
1548     case G_TYPE_UINT:
1549       ret = g_value_get_uint (value);
1550       break;
1551 
1552     case G_TYPE_INT:
1553       ret = g_value_get_int (value);
1554       break;
1555 
1556     case G_TYPE_INT64:
1557       ret = g_value_get_int64 (value);
1558       break;
1559 
1560     case G_TYPE_UINT64:
1561       ret = g_value_get_uint64 (value);
1562       break;
1563 
1564     default:
1565       goto return_invalid;
1566     }
1567 
1568   if (valid != NULL)
1569     *valid = TRUE;
1570 
1571   return ret;
1572 
1573 return_invalid:
1574   if (valid != NULL)
1575     *valid = FALSE;
1576 
1577   return 0;
1578 }
1579 
1580 /**
1581  * tp_asv_set_double: (skip)
1582  * @asv: a #GHashTable created with tp_asv_new()
1583  * @key: string key
1584  * @value: value
1585  *
1586  * Stores the value in the map.
1587  *
1588  * The value is stored as a slice-allocated GValue.
1589  *
1590  * See Also: tp_asv_new(), tp_asv_get_double(), tp_g_value_slice_new_double()
1591  * Since: 0.7.29
1592  */
1593 void
tp_asv_set_double(GHashTable * asv,const gchar * key,gdouble value)1594 tp_asv_set_double (GHashTable *asv,
1595                    const gchar *key,
1596                    gdouble value)
1597 {
1598   g_return_if_fail (asv != NULL);
1599   g_return_if_fail (key != NULL);
1600 
1601   g_hash_table_insert (asv, (char *) key, tp_g_value_slice_new_double (value));
1602 }
1603 
1604 /**
1605  * tp_asv_get_object_path:
1606  * @asv: (element-type utf8 GObject.Value): A GHashTable where the keys are
1607  * strings and the values are GValues
1608  * @key: The key to look up
1609  *
1610  * If a value for @key in @asv is present and is an object path, return it.
1611  *
1612  * Otherwise return %NULL.
1613  *
1614  * The returned value is not copied, and is only valid as long as the value
1615  * for @key in @asv is not removed or altered. Copy it with g_strdup() if you
1616  * need to keep it for longer.
1617  *
1618  * Returns: (transfer none) (allow-none): the object-path value of @key, or
1619  * %NULL
1620  * Since: 0.7.9
1621  */
1622 const gchar *
tp_asv_get_object_path(const GHashTable * asv,const gchar * key)1623 tp_asv_get_object_path (const GHashTable *asv,
1624                         const gchar *key)
1625 {
1626   GValue *value;
1627 
1628   g_return_val_if_fail (asv != NULL, 0);
1629   g_return_val_if_fail (key != NULL, 0);
1630 
1631   value = g_hash_table_lookup ((GHashTable *) asv, key);
1632 
1633   if (value == NULL || !G_VALUE_HOLDS (value, DBUS_TYPE_G_OBJECT_PATH))
1634     return NULL;
1635 
1636   return g_value_get_boxed (value);
1637 }
1638 
1639 /**
1640  * tp_asv_set_object_path: (skip)
1641  * @asv: a #GHashTable created with tp_asv_new()
1642  * @key: string key
1643  * @value: value
1644  *
1645  * Stores the value in the map.
1646  *
1647  * The value is stored as a slice-allocated GValue.
1648  *
1649  * See Also: tp_asv_new(), tp_asv_get_object_path(),
1650  * tp_g_value_slice_new_object_path()
1651  * Since: 0.7.29
1652  */
1653 void
tp_asv_set_object_path(GHashTable * asv,const gchar * key,const gchar * value)1654 tp_asv_set_object_path (GHashTable *asv,
1655                         const gchar *key,
1656                         const gchar *value)
1657 {
1658   g_return_if_fail (asv != NULL);
1659   g_return_if_fail (key != NULL);
1660 
1661   g_hash_table_insert (asv, (char *) key,
1662       tp_g_value_slice_new_object_path (value));
1663 }
1664 
1665 /**
1666  * tp_asv_take_object_path: (skip)
1667  * @asv: a #GHashTable created with tp_asv_new()
1668  * @key: string key
1669  * @value: value
1670  *
1671  * Stores the value in the map.
1672  *
1673  * The value is stored as a slice-allocated GValue.
1674  *
1675  * See Also: tp_asv_new(), tp_asv_get_object_path(),
1676  * tp_g_value_slice_new_take_object_path()
1677  * Since: 0.7.29
1678  */
1679 void
tp_asv_take_object_path(GHashTable * asv,const gchar * key,gchar * value)1680 tp_asv_take_object_path (GHashTable *asv,
1681                          const gchar *key,
1682                          gchar *value)
1683 {
1684   g_return_if_fail (asv != NULL);
1685   g_return_if_fail (key != NULL);
1686 
1687   g_hash_table_insert (asv, (char *) key,
1688       tp_g_value_slice_new_take_object_path (value));
1689 }
1690 
1691 /**
1692  * tp_asv_set_static_object_path: (skip)
1693  * @asv: a #GHashTable created with tp_asv_new()
1694  * @key: string key
1695  * @value: value
1696  *
1697  * Stores the value in the map.
1698  *
1699  * The value is stored as a slice-allocated GValue.
1700  *
1701  * See Also: tp_asv_new(), tp_asv_get_object_path(),
1702  * tp_g_value_slice_new_static_object_path()
1703  * Since: 0.7.29
1704  */
1705 void
tp_asv_set_static_object_path(GHashTable * asv,const gchar * key,const gchar * value)1706 tp_asv_set_static_object_path (GHashTable *asv,
1707                                const gchar *key,
1708                                const gchar *value)
1709 {
1710   g_return_if_fail (asv != NULL);
1711   g_return_if_fail (key != NULL);
1712 
1713   g_hash_table_insert (asv, (char *) key,
1714       tp_g_value_slice_new_static_object_path (value));
1715 }
1716 
1717 /**
1718  * tp_asv_get_boxed:
1719  * @asv: (element-type utf8 GObject.Value): A GHashTable where the keys are
1720  * strings and the values are GValues
1721  * @key: The key to look up
1722  * @type: The type that the key's value should have, which must be derived
1723  *  from %G_TYPE_BOXED
1724  *
1725  * If a value for @key in @asv is present and is of the desired type,
1726  * return it.
1727  *
1728  * Otherwise return %NULL.
1729  *
1730  * The returned value is not copied, and is only valid as long as the value
1731  * for @key in @asv is not removed or altered. Copy it, for instance with
1732  * g_boxed_copy(), if you need to keep it for longer.
1733  *
1734  * Returns: (transfer none) (allow-none): the value of @key, or %NULL
1735  * Since: 0.7.9
1736  */
1737 gpointer
tp_asv_get_boxed(const GHashTable * asv,const gchar * key,GType type)1738 tp_asv_get_boxed (const GHashTable *asv,
1739                   const gchar *key,
1740                   GType type)
1741 {
1742   GValue *value;
1743 
1744   g_return_val_if_fail (asv != NULL, NULL);
1745   g_return_val_if_fail (key != NULL, NULL);
1746   g_return_val_if_fail (G_TYPE_FUNDAMENTAL (type) == G_TYPE_BOXED, NULL);
1747 
1748   value = g_hash_table_lookup ((GHashTable *) asv, key);
1749 
1750   if (value == NULL || !G_VALUE_HOLDS (value, type))
1751     return NULL;
1752 
1753   return g_value_get_boxed (value);
1754 }
1755 
1756 /**
1757  * tp_asv_set_boxed: (skip)
1758  * @asv: a #GHashTable created with tp_asv_new()
1759  * @key: string key
1760  * @type: the type of the key's value, which must be derived from %G_TYPE_BOXED
1761  * @value: value
1762  *
1763  * Stores the value in the map.
1764  *
1765  * The value is stored as a slice-allocated GValue.
1766  *
1767  * See Also: tp_asv_new(), tp_asv_get_boxed(), tp_g_value_slice_new_boxed()
1768  * Since: 0.7.29
1769  */
1770 void
tp_asv_set_boxed(GHashTable * asv,const gchar * key,GType type,gconstpointer value)1771 tp_asv_set_boxed (GHashTable *asv,
1772                   const gchar *key,
1773                   GType type,
1774                   gconstpointer value)
1775 {
1776   g_return_if_fail (asv != NULL);
1777   g_return_if_fail (key != NULL);
1778   g_return_if_fail (G_TYPE_FUNDAMENTAL (type) == G_TYPE_BOXED);
1779 
1780   g_hash_table_insert (asv, (char *) key,
1781       tp_g_value_slice_new_boxed (type, value));
1782 }
1783 
1784 /**
1785  * tp_asv_take_boxed: (skip)
1786  * @asv: a #GHashTable created with tp_asv_new()
1787  * @key: string key
1788  * @type: the type of the key's value, which must be derived from %G_TYPE_BOXED
1789  * @value: value
1790  *
1791  * Stores the value in the map.
1792  *
1793  * The value is stored as a slice-allocated GValue.
1794  *
1795  * See Also: tp_asv_new(), tp_asv_get_boxed(), tp_g_value_slice_new_take_boxed()
1796  * Since: 0.7.29
1797  */
1798 void
tp_asv_take_boxed(GHashTable * asv,const gchar * key,GType type,gpointer value)1799 tp_asv_take_boxed (GHashTable *asv,
1800                    const gchar *key,
1801                    GType type,
1802                    gpointer value)
1803 {
1804   g_return_if_fail (asv != NULL);
1805   g_return_if_fail (key != NULL);
1806   g_return_if_fail (G_TYPE_FUNDAMENTAL (type) == G_TYPE_BOXED);
1807 
1808   g_hash_table_insert (asv, (char *) key,
1809       tp_g_value_slice_new_take_boxed (type, value));
1810 }
1811 
1812 /**
1813  * tp_asv_set_static_boxed: (skip)
1814  * @asv: a #GHashTable created with tp_asv_new()
1815  * @key: string key
1816  * @type: the type of the key's value, which must be derived from %G_TYPE_BOXED
1817  * @value: value
1818  *
1819  * Stores the value in the map.
1820  *
1821  * The value is stored as a slice-allocated GValue.
1822  *
1823  * See Also: tp_asv_new(), tp_asv_get_boxed(),
1824  * tp_g_value_slice_new_static_boxed()
1825  * Since: 0.7.29
1826  */
1827 void
tp_asv_set_static_boxed(GHashTable * asv,const gchar * key,GType type,gconstpointer value)1828 tp_asv_set_static_boxed (GHashTable *asv,
1829                          const gchar *key,
1830                          GType type,
1831                          gconstpointer value)
1832 {
1833   g_return_if_fail (asv != NULL);
1834   g_return_if_fail (key != NULL);
1835   g_return_if_fail (G_TYPE_FUNDAMENTAL (type) == G_TYPE_BOXED);
1836 
1837   g_hash_table_insert (asv, (char *) key,
1838       tp_g_value_slice_new_static_boxed (type, value));
1839 }
1840 
1841 /**
1842  * tp_asv_get_strv:
1843  * @asv: (element-type utf8 GObject.Value): A GHashTable where the keys are
1844  * strings and the values are GValues
1845  * @key: The key to look up
1846  *
1847  * If a value for @key in @asv is present and is an array of strings (strv),
1848  * return it.
1849  *
1850  * Otherwise return %NULL.
1851  *
1852  * The returned value is not copied, and is only valid as long as the value
1853  * for @key in @asv is not removed or altered. Copy it with g_strdupv() if you
1854  * need to keep it for longer.
1855  *
1856  * Returns: (transfer none) (allow-none): the %NULL-terminated string-array
1857  * value of @key, or %NULL
1858  * Since: 0.7.9
1859  */
1860 const gchar * const *
tp_asv_get_strv(const GHashTable * asv,const gchar * key)1861 tp_asv_get_strv (const GHashTable *asv,
1862                  const gchar *key)
1863 {
1864   GValue *value;
1865 
1866   g_return_val_if_fail (asv != NULL, NULL);
1867   g_return_val_if_fail (key != NULL, NULL);
1868 
1869   value = g_hash_table_lookup ((GHashTable *) asv, key);
1870 
1871   if (value == NULL || !G_VALUE_HOLDS (value, G_TYPE_STRV))
1872     return NULL;
1873 
1874   return g_value_get_boxed (value);
1875 }
1876 
1877 /**
1878  * tp_asv_set_strv: (skip)
1879  * @asv: a #GHashTable created with tp_asv_new()
1880  * @key: string key
1881  * @value: a %NULL-terminated string array
1882  *
1883  * Stores the value in the map.
1884  *
1885  * The value is stored as a slice-allocated GValue.
1886  *
1887  * See Also: tp_asv_new(), tp_asv_get_strv()
1888  * Since: 0.7.29
1889  */
1890 void
tp_asv_set_strv(GHashTable * asv,const gchar * key,gchar ** value)1891 tp_asv_set_strv (GHashTable *asv,
1892                  const gchar *key,
1893                  gchar **value)
1894 {
1895   g_return_if_fail (asv != NULL);
1896   g_return_if_fail (key != NULL);
1897 
1898   g_hash_table_insert (asv, (char *) key,
1899       tp_g_value_slice_new_boxed (G_TYPE_STRV, value));
1900 }
1901 
1902 /**
1903  * tp_asv_lookup: (skip)
1904  * @asv: A GHashTable where the keys are strings and the values are GValues
1905  * @key: The key to look up
1906  *
1907  * If a value for @key in @asv is present, return it. Otherwise return %NULL.
1908  *
1909  * The returned value is not copied, and is only valid as long as the value
1910  * for @key in @asv is not removed or altered. Copy it with (for instance)
1911  * g_value_copy() if you need to keep it for longer.
1912  *
1913  * Returns: the value of @key, or %NULL
1914  * Since: 0.7.9
1915  */
1916 const GValue *
tp_asv_lookup(const GHashTable * asv,const gchar * key)1917 tp_asv_lookup (const GHashTable *asv,
1918                const gchar *key)
1919 {
1920   g_return_val_if_fail (asv != NULL, NULL);
1921   g_return_val_if_fail (key != NULL, NULL);
1922 
1923   return g_hash_table_lookup ((GHashTable *) asv, key);
1924 }
1925 
1926 /**
1927  * tp_asv_dump: (skip)
1928  * @asv: a #GHashTable created with tp_asv_new()
1929  *
1930  * Dumps the a{sv} map to the debugging console.
1931  *
1932  * The purpose of this function is give the programmer the ability to easily
1933  * inspect the contents of an a{sv} map for debugging purposes.
1934  */
1935 void
tp_asv_dump(GHashTable * asv)1936 tp_asv_dump (GHashTable *asv)
1937 {
1938   GHashTableIter iter;
1939   char *key;
1940   GValue *value;
1941 
1942   g_return_if_fail (asv != NULL);
1943 
1944   g_debug ("{");
1945 
1946   g_hash_table_iter_init (&iter, asv);
1947   while (g_hash_table_iter_next (&iter, (gpointer) &key, (gpointer) &value))
1948   {
1949     char *str = g_strdup_value_contents (value);
1950     g_debug ("  '%s' : %s", key, str);
1951     g_free (str);
1952   }
1953 
1954   g_debug ("}");
1955 }
1956