• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* GDBus - GLib D-Bus Library
2  *
3  * Copyright (C) 2008-2010 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.1 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, see <http://www.gnu.org/licenses/>.
17  *
18  * Author: David Zeuthen <davidz@redhat.com>
19  */
20 
21 #include "config.h"
22 
23 #include <stdlib.h>
24 #include <string.h>
25 
26 #include "gdbusintrospection.h"
27 
28 #include "glibintl.h"
29 
30 /**
31  * SECTION:gdbusintrospection
32  * @title: D-Bus Introspection Data
33  * @short_description: Node and interface description data structures
34  * @include: gio/gio.h
35  *
36  * Various data structures and convenience routines to parse and
37  * generate D-Bus introspection XML. Introspection information is
38  * used when registering objects with g_dbus_connection_register_object().
39  *
40  * The format of D-Bus introspection XML is specified in the
41  * [D-Bus specification](http://dbus.freedesktop.org/doc/dbus-specification.html#introspection-format)
42  */
43 
44 /* ---------------------------------------------------------------------------------------------------- */
45 
46 #define _MY_DEFINE_BOXED_TYPE(TypeName, type_name) \
47   G_DEFINE_BOXED_TYPE (TypeName, type_name, type_name##_ref, type_name##_unref)
48 
49 _MY_DEFINE_BOXED_TYPE (GDBusNodeInfo,       g_dbus_node_info)
50 _MY_DEFINE_BOXED_TYPE (GDBusInterfaceInfo,  g_dbus_interface_info)
51 _MY_DEFINE_BOXED_TYPE (GDBusMethodInfo,     g_dbus_method_info)
52 _MY_DEFINE_BOXED_TYPE (GDBusSignalInfo,     g_dbus_signal_info)
53 _MY_DEFINE_BOXED_TYPE (GDBusPropertyInfo,   g_dbus_property_info)
54 _MY_DEFINE_BOXED_TYPE (GDBusArgInfo,        g_dbus_arg_info)
55 _MY_DEFINE_BOXED_TYPE (GDBusAnnotationInfo, g_dbus_annotation_info)
56 
57 #undef _MY_DEFINE_BOXED_TYPE
58 
59 /* ---------------------------------------------------------------------------------------------------- */
60 
61 typedef struct
62 {
63   /* stuff we are currently collecting */
64   GPtrArray *args;
65   GPtrArray *out_args;
66   GPtrArray *methods;
67   GPtrArray *signals;
68   GPtrArray *properties;
69   GPtrArray *interfaces;
70   GPtrArray *nodes;
71   GPtrArray *annotations;
72 
73   /* A list of GPtrArray's containing annotations */
74   GSList *annotations_stack;
75 
76   /* A list of GPtrArray's containing interfaces */
77   GSList *interfaces_stack;
78 
79   /* A list of GPtrArray's containing nodes */
80   GSList *nodes_stack;
81 
82   /* Whether the direction was "in" for last parsed arg */
83   gboolean last_arg_was_in;
84 
85   /* Number of args currently being collected; used for assigning
86    * names to args without a "name" attribute
87    */
88   guint num_args;
89 
90 } ParseData;
91 
92 /* ---------------------------------------------------------------------------------------------------- */
93 
94 /**
95  * g_dbus_node_info_ref:
96  * @info: A #GDBusNodeInfo
97  *
98  * If @info is statically allocated does nothing. Otherwise increases
99  * the reference count.
100  *
101  * Returns: The same @info.
102  *
103  * Since: 2.26
104  */
105 GDBusNodeInfo *
g_dbus_node_info_ref(GDBusNodeInfo * info)106 g_dbus_node_info_ref (GDBusNodeInfo *info)
107 {
108   if (g_atomic_int_get (&info->ref_count) == -1)
109     return info;
110   g_atomic_int_inc (&info->ref_count);
111   return info;
112 }
113 
114 /**
115  * g_dbus_interface_info_ref:
116  * @info: A #GDBusInterfaceInfo
117  *
118  * If @info is statically allocated does nothing. Otherwise increases
119  * the reference count.
120  *
121  * Returns: The same @info.
122  *
123  * Since: 2.26
124  */
125 GDBusInterfaceInfo *
g_dbus_interface_info_ref(GDBusInterfaceInfo * info)126 g_dbus_interface_info_ref (GDBusInterfaceInfo *info)
127 {
128   if (g_atomic_int_get (&info->ref_count) == -1)
129     return info;
130   g_atomic_int_inc (&info->ref_count);
131   return info;
132 }
133 
134 /**
135  * g_dbus_method_info_ref:
136  * @info: A #GDBusMethodInfo
137  *
138  * If @info is statically allocated does nothing. Otherwise increases
139  * the reference count.
140  *
141  * Returns: The same @info.
142  *
143  * Since: 2.26
144  */
145 GDBusMethodInfo *
g_dbus_method_info_ref(GDBusMethodInfo * info)146 g_dbus_method_info_ref (GDBusMethodInfo *info)
147 {
148   if (g_atomic_int_get (&info->ref_count) == -1)
149     return info;
150   g_atomic_int_inc (&info->ref_count);
151   return info;
152 }
153 
154 /**
155  * g_dbus_signal_info_ref:
156  * @info: A #GDBusSignalInfo
157  *
158  * If @info is statically allocated does nothing. Otherwise increases
159  * the reference count.
160  *
161  * Returns: The same @info.
162  *
163  * Since: 2.26
164  */
165 GDBusSignalInfo *
g_dbus_signal_info_ref(GDBusSignalInfo * info)166 g_dbus_signal_info_ref (GDBusSignalInfo *info)
167 {
168   if (g_atomic_int_get (&info->ref_count) == -1)
169     return info;
170   g_atomic_int_inc (&info->ref_count);
171   return info;
172 }
173 
174 /**
175  * g_dbus_property_info_ref:
176  * @info: A #GDBusPropertyInfo
177  *
178  * If @info is statically allocated does nothing. Otherwise increases
179  * the reference count.
180  *
181  * Returns: The same @info.
182  *
183  * Since: 2.26
184  */
185 GDBusPropertyInfo *
g_dbus_property_info_ref(GDBusPropertyInfo * info)186 g_dbus_property_info_ref (GDBusPropertyInfo *info)
187 {
188   if (g_atomic_int_get (&info->ref_count) == -1)
189     return info;
190   g_atomic_int_inc (&info->ref_count);
191   return info;
192 }
193 
194 /**
195  * g_dbus_arg_info_ref:
196  * @info: A #GDBusArgInfo
197  *
198  * If @info is statically allocated does nothing. Otherwise increases
199  * the reference count.
200  *
201  * Returns: The same @info.
202  *
203  * Since: 2.26
204  */
205 GDBusArgInfo *
g_dbus_arg_info_ref(GDBusArgInfo * info)206 g_dbus_arg_info_ref (GDBusArgInfo *info)
207 {
208   if (g_atomic_int_get (&info->ref_count) == -1)
209     return info;
210   g_atomic_int_inc (&info->ref_count);
211   return info;
212 }
213 
214 /**
215  * g_dbus_annotation_info_ref:
216  * @info: A #GDBusNodeInfo
217  *
218  * If @info is statically allocated does nothing. Otherwise increases
219  * the reference count.
220  *
221  * Returns: The same @info.
222  *
223  * Since: 2.26
224  */
225 GDBusAnnotationInfo *
g_dbus_annotation_info_ref(GDBusAnnotationInfo * info)226 g_dbus_annotation_info_ref (GDBusAnnotationInfo *info)
227 {
228   if (g_atomic_int_get (&info->ref_count) == -1)
229     return info;
230   g_atomic_int_inc (&info->ref_count);
231   return info;
232 }
233 
234 /* ---------------------------------------------------------------------------------------------------- */
235 
236 static void
free_null_terminated_array(gpointer array,GDestroyNotify unref_func)237 free_null_terminated_array (gpointer array, GDestroyNotify unref_func)
238 {
239   guint n;
240   gpointer *p = array;
241   if (p == NULL)
242     return;
243   for (n = 0; p[n] != NULL; n++)
244     unref_func (p[n]);
245   g_free (p);
246 }
247 
248 /**
249  * g_dbus_annotation_info_unref:
250  * @info: A #GDBusAnnotationInfo.
251  *
252  * If @info is statically allocated, does nothing. Otherwise decreases
253  * the reference count of @info. When its reference count drops to 0,
254  * the memory used is freed.
255  *
256  * Since: 2.26
257  */
258 void
g_dbus_annotation_info_unref(GDBusAnnotationInfo * info)259 g_dbus_annotation_info_unref (GDBusAnnotationInfo *info)
260 {
261   if (g_atomic_int_get (&info->ref_count) == -1)
262     return;
263   if (g_atomic_int_dec_and_test (&info->ref_count))
264     {
265       g_free (info->key);
266       g_free (info->value);
267       free_null_terminated_array (info->annotations, (GDestroyNotify) g_dbus_annotation_info_unref);
268       g_free (info);
269     }
270 }
271 
272 /**
273  * g_dbus_arg_info_unref:
274  * @info: A #GDBusArgInfo.
275  *
276  * If @info is statically allocated, does nothing. Otherwise decreases
277  * the reference count of @info. When its reference count drops to 0,
278  * the memory used is freed.
279  *
280  * Since: 2.26
281  */
282 void
g_dbus_arg_info_unref(GDBusArgInfo * info)283 g_dbus_arg_info_unref (GDBusArgInfo *info)
284 {
285   if (g_atomic_int_get (&info->ref_count) == -1)
286     return;
287   if (g_atomic_int_dec_and_test (&info->ref_count))
288     {
289       g_free (info->name);
290       g_free (info->signature);
291       free_null_terminated_array (info->annotations, (GDestroyNotify) g_dbus_annotation_info_unref);
292       g_free (info);
293     }
294 }
295 
296 /**
297  * g_dbus_method_info_unref:
298  * @info: A #GDBusMethodInfo.
299  *
300  * If @info is statically allocated, does nothing. Otherwise decreases
301  * the reference count of @info. When its reference count drops to 0,
302  * the memory used is freed.
303  *
304  * Since: 2.26
305  */
306 void
g_dbus_method_info_unref(GDBusMethodInfo * info)307 g_dbus_method_info_unref (GDBusMethodInfo *info)
308 {
309   if (g_atomic_int_get (&info->ref_count) == -1)
310     return;
311   if (g_atomic_int_dec_and_test (&info->ref_count))
312     {
313       g_free (info->name);
314       free_null_terminated_array (info->in_args, (GDestroyNotify) g_dbus_arg_info_unref);
315       free_null_terminated_array (info->out_args, (GDestroyNotify) g_dbus_arg_info_unref);
316       free_null_terminated_array (info->annotations, (GDestroyNotify) g_dbus_annotation_info_unref);
317       g_free (info);
318     }
319 }
320 
321 /**
322  * g_dbus_signal_info_unref:
323  * @info: A #GDBusSignalInfo.
324  *
325  * If @info is statically allocated, does nothing. Otherwise decreases
326  * the reference count of @info. When its reference count drops to 0,
327  * the memory used is freed.
328  *
329  * Since: 2.26
330  */
331 void
g_dbus_signal_info_unref(GDBusSignalInfo * info)332 g_dbus_signal_info_unref (GDBusSignalInfo *info)
333 {
334   if (g_atomic_int_get (&info->ref_count) == -1)
335     return;
336   if (g_atomic_int_dec_and_test (&info->ref_count))
337     {
338       g_free (info->name);
339       free_null_terminated_array (info->args, (GDestroyNotify) g_dbus_arg_info_unref);
340       free_null_terminated_array (info->annotations, (GDestroyNotify) g_dbus_annotation_info_unref);
341       g_free (info);
342     }
343 }
344 
345 /**
346  * g_dbus_property_info_unref:
347  * @info: A #GDBusPropertyInfo.
348  *
349  * If @info is statically allocated, does nothing. Otherwise decreases
350  * the reference count of @info. When its reference count drops to 0,
351  * the memory used is freed.
352  *
353  * Since: 2.26
354  */
355 void
g_dbus_property_info_unref(GDBusPropertyInfo * info)356 g_dbus_property_info_unref (GDBusPropertyInfo *info)
357 {
358   if (g_atomic_int_get (&info->ref_count) == -1)
359     return;
360   if (g_atomic_int_dec_and_test (&info->ref_count))
361     {
362       g_free (info->name);
363       g_free (info->signature);
364       free_null_terminated_array (info->annotations, (GDestroyNotify) g_dbus_annotation_info_unref);
365       g_free (info);
366     }
367 }
368 
369 /**
370  * g_dbus_interface_info_unref:
371  * @info: A #GDBusInterfaceInfo.
372  *
373  * If @info is statically allocated, does nothing. Otherwise decreases
374  * the reference count of @info. When its reference count drops to 0,
375  * the memory used is freed.
376  *
377  * Since: 2.26
378  */
379 void
g_dbus_interface_info_unref(GDBusInterfaceInfo * info)380 g_dbus_interface_info_unref (GDBusInterfaceInfo *info)
381 {
382   if (g_atomic_int_get (&info->ref_count) == -1)
383     return;
384   if (g_atomic_int_dec_and_test (&info->ref_count))
385     {
386       g_free (info->name);
387       free_null_terminated_array (info->methods, (GDestroyNotify) g_dbus_method_info_unref);
388       free_null_terminated_array (info->signals, (GDestroyNotify) g_dbus_signal_info_unref);
389       free_null_terminated_array (info->properties, (GDestroyNotify) g_dbus_property_info_unref);
390       free_null_terminated_array (info->annotations, (GDestroyNotify) g_dbus_annotation_info_unref);
391       g_free (info);
392     }
393 }
394 
395 /**
396  * g_dbus_node_info_unref:
397  * @info: A #GDBusNodeInfo.
398  *
399  * If @info is statically allocated, does nothing. Otherwise decreases
400  * the reference count of @info. When its reference count drops to 0,
401  * the memory used is freed.
402  *
403  * Since: 2.26
404  */
405 void
g_dbus_node_info_unref(GDBusNodeInfo * info)406 g_dbus_node_info_unref (GDBusNodeInfo *info)
407 {
408   if (g_atomic_int_get (&info->ref_count) == -1)
409     return;
410   if (g_atomic_int_dec_and_test (&info->ref_count))
411     {
412       g_free (info->path);
413       free_null_terminated_array (info->interfaces, (GDestroyNotify) g_dbus_interface_info_unref);
414       free_null_terminated_array (info->nodes, (GDestroyNotify) g_dbus_node_info_unref);
415       free_null_terminated_array (info->annotations, (GDestroyNotify) g_dbus_annotation_info_unref);
416       g_free (info);
417     }
418 }
419 
420 /* ---------------------------------------------------------------------------------------------------- */
421 
422 static void
g_dbus_annotation_info_set(ParseData * data,GDBusAnnotationInfo * info,const gchar * key,const gchar * value,GDBusAnnotationInfo ** embedded_annotations)423 g_dbus_annotation_info_set (ParseData            *data,
424                             GDBusAnnotationInfo  *info,
425                             const gchar          *key,
426                             const gchar          *value,
427                             GDBusAnnotationInfo **embedded_annotations)
428 {
429   info->ref_count = 1;
430 
431   if (key != NULL)
432     info->key = g_strdup (key);
433 
434   if (value != NULL)
435     info->value = g_strdup (value);
436 
437   if (embedded_annotations != NULL)
438     info->annotations = embedded_annotations;
439 }
440 
441 static void
g_dbus_arg_info_set(ParseData * data,GDBusArgInfo * info,const gchar * name,const gchar * signature,GDBusAnnotationInfo ** annotations)442 g_dbus_arg_info_set (ParseData            *data,
443                      GDBusArgInfo         *info,
444                      const gchar          *name,
445                      const gchar          *signature,
446                      GDBusAnnotationInfo **annotations)
447 {
448   info->ref_count = 1;
449 
450   /* name may be NULL - TODO: compute name? */
451   if (name != NULL)
452     info->name = g_strdup (name);
453 
454   if (signature != NULL)
455     info->signature = g_strdup (signature);
456 
457   if (annotations != NULL)
458     info->annotations = annotations;
459 }
460 
461 static void
g_dbus_method_info_set(ParseData * data,GDBusMethodInfo * info,const gchar * name,GDBusArgInfo ** in_args,GDBusArgInfo ** out_args,GDBusAnnotationInfo ** annotations)462 g_dbus_method_info_set (ParseData            *data,
463                         GDBusMethodInfo      *info,
464                         const gchar          *name,
465                         GDBusArgInfo        **in_args,
466                         GDBusArgInfo        **out_args,
467                         GDBusAnnotationInfo **annotations)
468 {
469   info->ref_count = 1;
470 
471   if (name != NULL)
472     info->name = g_strdup (name);
473 
474   if (in_args != NULL)
475     info->in_args = in_args;
476 
477   if (out_args != NULL)
478     info->out_args = out_args;
479 
480   if (annotations != NULL)
481     info->annotations = annotations;
482 }
483 
484 static void
g_dbus_signal_info_set(ParseData * data,GDBusSignalInfo * info,const gchar * name,GDBusArgInfo ** args,GDBusAnnotationInfo ** annotations)485 g_dbus_signal_info_set (ParseData            *data,
486                         GDBusSignalInfo      *info,
487                         const gchar          *name,
488                         GDBusArgInfo        **args,
489                         GDBusAnnotationInfo **annotations)
490 {
491   info->ref_count = 1;
492 
493   if (name != NULL)
494     info->name = g_strdup (name);
495 
496   if (args != NULL)
497     info->args = args;
498 
499   if (annotations != NULL)
500     info->annotations = annotations;
501 }
502 
503 static void
g_dbus_property_info_set(ParseData * data,GDBusPropertyInfo * info,const gchar * name,const gchar * signature,GDBusPropertyInfoFlags flags,GDBusAnnotationInfo ** annotations)504 g_dbus_property_info_set (ParseData               *data,
505                           GDBusPropertyInfo       *info,
506                           const gchar             *name,
507                           const gchar             *signature,
508                           GDBusPropertyInfoFlags   flags,
509                           GDBusAnnotationInfo    **annotations)
510 {
511   info->ref_count = 1;
512 
513   if (name != NULL)
514     info->name = g_strdup (name);
515 
516   if (flags != G_DBUS_PROPERTY_INFO_FLAGS_NONE)
517     info->flags = flags;
518 
519   if (signature != NULL)
520     info->signature = g_strdup (signature);
521 
522   if (annotations != NULL)
523     info->annotations = annotations;
524 }
525 
526 static void
g_dbus_interface_info_set(ParseData * data,GDBusInterfaceInfo * info,const gchar * name,GDBusMethodInfo ** methods,GDBusSignalInfo ** signals,GDBusPropertyInfo ** properties,GDBusAnnotationInfo ** annotations)527 g_dbus_interface_info_set (ParseData            *data,
528                            GDBusInterfaceInfo   *info,
529                            const gchar          *name,
530                            GDBusMethodInfo     **methods,
531                            GDBusSignalInfo     **signals,
532                            GDBusPropertyInfo   **properties,
533                            GDBusAnnotationInfo **annotations)
534 {
535   info->ref_count = 1;
536 
537   if (name != NULL)
538     info->name = g_strdup (name);
539 
540   if (methods != NULL)
541     info->methods = methods;
542 
543   if (signals != NULL)
544     info->signals = signals;
545 
546   if (properties != NULL)
547     info->properties = properties;
548 
549   if (annotations != NULL)
550     info->annotations = annotations;
551 }
552 
553 static void
g_dbus_node_info_set(ParseData * data,GDBusNodeInfo * info,const gchar * path,GDBusInterfaceInfo ** interfaces,GDBusNodeInfo ** nodes,GDBusAnnotationInfo ** annotations)554 g_dbus_node_info_set (ParseData            *data,
555                       GDBusNodeInfo        *info,
556                       const gchar          *path,
557                       GDBusInterfaceInfo  **interfaces,
558                       GDBusNodeInfo       **nodes,
559                       GDBusAnnotationInfo **annotations)
560 {
561   info->ref_count = 1;
562 
563   if (path != NULL)
564     {
565       info->path = g_strdup (path);
566       /* TODO: relative / absolute path snafu */
567     }
568 
569   if (interfaces != NULL)
570     info->interfaces = interfaces;
571 
572   if (nodes != NULL)
573     info->nodes = nodes;
574 
575   if (annotations != NULL)
576     info->annotations = annotations;
577 }
578 
579 /* ---------------------------------------------------------------------------------------------------- */
580 
581 static void
g_dbus_annotation_info_generate_xml(GDBusAnnotationInfo * info,guint indent,GString * string_builder)582 g_dbus_annotation_info_generate_xml (GDBusAnnotationInfo *info,
583                                      guint                indent,
584                                      GString             *string_builder)
585 {
586   gchar *tmp;
587   guint n;
588 
589   tmp = g_markup_printf_escaped ("%*s<annotation name=\"%s\" value=\"%s\"",
590                                  indent, "",
591                                  info->key,
592                                  info->value);
593   g_string_append (string_builder, tmp);
594   g_free (tmp);
595 
596   if (info->annotations == NULL)
597     {
598       g_string_append (string_builder, "/>\n");
599     }
600   else
601     {
602       g_string_append (string_builder, ">\n");
603 
604       for (n = 0; info->annotations != NULL && info->annotations[n] != NULL; n++)
605         g_dbus_annotation_info_generate_xml (info->annotations[n],
606                                              indent + 2,
607                                              string_builder);
608 
609       g_string_append_printf (string_builder, "%*s</annotation>\n",
610                               indent, "");
611     }
612 
613 }
614 
615 static void
g_dbus_arg_info_generate_xml(GDBusArgInfo * info,guint indent,const gchar * extra_attributes,GString * string_builder)616 g_dbus_arg_info_generate_xml (GDBusArgInfo *info,
617                               guint         indent,
618                               const gchar  *extra_attributes,
619                               GString      *string_builder)
620 {
621   guint n;
622 
623   g_string_append_printf (string_builder, "%*s<arg type=\"%s\"",
624                           indent, "",
625                           info->signature);
626 
627   if (info->name != NULL)
628     g_string_append_printf (string_builder, " name=\"%s\"", info->name);
629 
630   if (extra_attributes != NULL)
631     g_string_append_printf (string_builder, " %s", extra_attributes);
632 
633   if (info->annotations == NULL)
634     {
635       g_string_append (string_builder, "/>\n");
636     }
637   else
638     {
639       g_string_append (string_builder, ">\n");
640 
641       for (n = 0; info->annotations != NULL && info->annotations[n] != NULL; n++)
642         g_dbus_annotation_info_generate_xml (info->annotations[n],
643                                              indent + 2,
644                                              string_builder);
645 
646       g_string_append_printf (string_builder, "%*s</arg>\n", indent, "");
647     }
648 
649 }
650 
651 static void
g_dbus_method_info_generate_xml(GDBusMethodInfo * info,guint indent,GString * string_builder)652 g_dbus_method_info_generate_xml (GDBusMethodInfo *info,
653                                  guint            indent,
654                                  GString         *string_builder)
655 {
656   guint n;
657 
658   g_string_append_printf (string_builder, "%*s<method name=\"%s\"",
659                           indent, "",
660                           info->name);
661 
662   if (info->annotations == NULL && info->in_args == NULL && info->out_args == NULL)
663     {
664       g_string_append (string_builder, "/>\n");
665     }
666   else
667     {
668       g_string_append (string_builder, ">\n");
669 
670       for (n = 0; info->annotations != NULL && info->annotations[n] != NULL; n++)
671         g_dbus_annotation_info_generate_xml (info->annotations[n],
672                                              indent + 2,
673                                              string_builder);
674 
675       for (n = 0; info->in_args != NULL && info->in_args[n] != NULL; n++)
676         g_dbus_arg_info_generate_xml (info->in_args[n],
677                                       indent + 2,
678                                       "direction=\"in\"",
679                                       string_builder);
680 
681       for (n = 0; info->out_args != NULL && info->out_args[n] != NULL; n++)
682         g_dbus_arg_info_generate_xml (info->out_args[n],
683                                       indent + 2,
684                                       "direction=\"out\"",
685                                       string_builder);
686 
687       g_string_append_printf (string_builder, "%*s</method>\n", indent, "");
688     }
689 }
690 
691 static void
g_dbus_signal_info_generate_xml(GDBusSignalInfo * info,guint indent,GString * string_builder)692 g_dbus_signal_info_generate_xml (GDBusSignalInfo *info,
693                                  guint            indent,
694                                  GString         *string_builder)
695 {
696   guint n;
697 
698   g_string_append_printf (string_builder, "%*s<signal name=\"%s\"",
699                           indent, "",
700                           info->name);
701 
702   if (info->annotations == NULL && info->args == NULL)
703     {
704       g_string_append (string_builder, "/>\n");
705     }
706   else
707     {
708       g_string_append (string_builder, ">\n");
709 
710       for (n = 0; info->annotations != NULL && info->annotations[n] != NULL; n++)
711         g_dbus_annotation_info_generate_xml (info->annotations[n],
712                                              indent + 2,
713                                              string_builder);
714 
715       for (n = 0; info->args != NULL && info->args[n] != NULL; n++)
716         g_dbus_arg_info_generate_xml (info->args[n],
717                                       indent + 2,
718                                       NULL,
719                                       string_builder);
720 
721       g_string_append_printf (string_builder, "%*s</signal>\n", indent, "");
722     }
723 }
724 
725 static void
g_dbus_property_info_generate_xml(GDBusPropertyInfo * info,guint indent,GString * string_builder)726 g_dbus_property_info_generate_xml (GDBusPropertyInfo *info,
727                                    guint              indent,
728                                    GString           *string_builder)
729 {
730   guint n;
731   const gchar *access_string;
732 
733   if ((info->flags & G_DBUS_PROPERTY_INFO_FLAGS_READABLE) &&
734       (info->flags & G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE))
735     {
736       access_string = "readwrite";
737     }
738   else if (info->flags & G_DBUS_PROPERTY_INFO_FLAGS_READABLE)
739     {
740       access_string = "read";
741     }
742   else if (info->flags & G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE)
743     {
744       access_string = "write";
745     }
746   else
747     {
748       g_assert_not_reached ();
749     }
750 
751   g_string_append_printf (string_builder, "%*s<property type=\"%s\" name=\"%s\" access=\"%s\"",
752                           indent, "",
753                           info->signature,
754                           info->name,
755                           access_string);
756 
757   if (info->annotations == NULL)
758     {
759       g_string_append (string_builder, "/>\n");
760     }
761   else
762     {
763       g_string_append (string_builder, ">\n");
764 
765       for (n = 0; info->annotations != NULL && info->annotations[n] != NULL; n++)
766         g_dbus_annotation_info_generate_xml (info->annotations[n],
767                                                indent + 2,
768                                                string_builder);
769 
770       g_string_append_printf (string_builder, "%*s</property>\n", indent, "");
771     }
772 
773 }
774 
775 /**
776  * g_dbus_interface_info_generate_xml:
777  * @info: A #GDBusNodeInfo
778  * @indent: Indentation level.
779  * @string_builder: A #GString to to append XML data to.
780  *
781  * Appends an XML representation of @info (and its children) to @string_builder.
782  *
783  * This function is typically used for generating introspection XML
784  * documents at run-time for handling the
785  * `org.freedesktop.DBus.Introspectable.Introspect`
786  * method.
787  *
788  * Since: 2.26
789  */
790 void
g_dbus_interface_info_generate_xml(GDBusInterfaceInfo * info,guint indent,GString * string_builder)791 g_dbus_interface_info_generate_xml (GDBusInterfaceInfo *info,
792                                     guint               indent,
793                                     GString            *string_builder)
794 {
795   guint n;
796 
797   g_string_append_printf (string_builder, "%*s<interface name=\"%s\">\n",
798                           indent, "",
799                           info->name);
800 
801   for (n = 0; info->annotations != NULL && info->annotations[n] != NULL; n++)
802     g_dbus_annotation_info_generate_xml (info->annotations[n],
803                                          indent + 2,
804                                          string_builder);
805 
806   for (n = 0; info->methods != NULL && info->methods[n] != NULL; n++)
807     g_dbus_method_info_generate_xml (info->methods[n],
808                                      indent + 2,
809                                      string_builder);
810 
811   for (n = 0; info->signals != NULL && info->signals[n] != NULL; n++)
812     g_dbus_signal_info_generate_xml (info->signals[n],
813                                      indent + 2,
814                                      string_builder);
815 
816   for (n = 0; info->properties != NULL && info->properties[n] != NULL; n++)
817     g_dbus_property_info_generate_xml (info->properties[n],
818                                        indent + 2,
819                                        string_builder);
820 
821   g_string_append_printf (string_builder, "%*s</interface>\n", indent, "");
822 }
823 
824 /**
825  * g_dbus_node_info_generate_xml:
826  * @info: A #GDBusNodeInfo.
827  * @indent: Indentation level.
828  * @string_builder: A #GString to to append XML data to.
829  *
830  * Appends an XML representation of @info (and its children) to @string_builder.
831  *
832  * This function is typically used for generating introspection XML documents at run-time for
833  * handling the `org.freedesktop.DBus.Introspectable.Introspect`  method.
834  *
835  * Since: 2.26
836  */
837 void
g_dbus_node_info_generate_xml(GDBusNodeInfo * info,guint indent,GString * string_builder)838 g_dbus_node_info_generate_xml (GDBusNodeInfo *info,
839                                guint          indent,
840                                GString       *string_builder)
841 {
842   guint n;
843 
844   g_string_append_printf (string_builder, "%*s<node", indent, "");
845   if (info->path != NULL)
846     g_string_append_printf (string_builder, " name=\"%s\"", info->path);
847 
848   if (info->interfaces == NULL && info->nodes == NULL && info->annotations == NULL)
849     {
850       g_string_append (string_builder, "/>\n");
851     }
852   else
853     {
854       g_string_append (string_builder, ">\n");
855 
856       for (n = 0; info->annotations != NULL && info->annotations[n] != NULL; n++)
857         g_dbus_annotation_info_generate_xml (info->annotations[n],
858                                              indent + 2,
859                                              string_builder);
860 
861       for (n = 0; info->interfaces != NULL && info->interfaces[n] != NULL; n++)
862         g_dbus_interface_info_generate_xml (info->interfaces[n],
863                                             indent + 2,
864                                             string_builder);
865 
866       for (n = 0; info->nodes != NULL && info->nodes[n] != NULL; n++)
867         g_dbus_node_info_generate_xml (info->nodes[n],
868                                        indent + 2,
869                                        string_builder);
870 
871       g_string_append_printf (string_builder, "%*s</node>\n", indent, "");
872     }
873 }
874 
875 /* ---------------------------------------------------------------------------------------------------- */
876 
877 static GDBusAnnotationInfo **
parse_data_steal_annotations(ParseData * data,guint * out_num_elements)878 parse_data_steal_annotations (ParseData *data,
879                               guint     *out_num_elements)
880 {
881   GDBusAnnotationInfo **ret;
882   if (out_num_elements != NULL)
883     *out_num_elements = data->annotations->len;
884   if (data->annotations == NULL)
885     ret = NULL;
886   else
887     {
888       g_ptr_array_add (data->annotations, NULL);
889       ret = (GDBusAnnotationInfo **) g_ptr_array_free (data->annotations, FALSE);
890     }
891   data->annotations = g_ptr_array_new ();
892   return ret;
893 }
894 
895 static GDBusArgInfo **
parse_data_steal_args(ParseData * data,guint * out_num_elements)896 parse_data_steal_args (ParseData *data,
897                        guint     *out_num_elements)
898 {
899   GDBusArgInfo **ret;
900   if (out_num_elements != NULL)
901     *out_num_elements = data->args->len;
902   if (data->args == NULL)
903     ret = NULL;
904   else
905     {
906       g_ptr_array_add (data->args, NULL);
907       ret = (GDBusArgInfo **) g_ptr_array_free (data->args, FALSE);
908     }
909   data->args = g_ptr_array_new ();
910   return ret;
911 }
912 
913 static GDBusArgInfo **
parse_data_steal_out_args(ParseData * data,guint * out_num_elements)914 parse_data_steal_out_args (ParseData *data,
915                            guint     *out_num_elements)
916 {
917   GDBusArgInfo **ret;
918   if (out_num_elements != NULL)
919     *out_num_elements = data->out_args->len;
920   if (data->out_args == NULL)
921     ret = NULL;
922   else
923     {
924       g_ptr_array_add (data->out_args, NULL);
925       ret = (GDBusArgInfo **) g_ptr_array_free (data->out_args, FALSE);
926     }
927   data->out_args = g_ptr_array_new ();
928   return ret;
929 }
930 
931 static GDBusMethodInfo **
parse_data_steal_methods(ParseData * data,guint * out_num_elements)932 parse_data_steal_methods (ParseData *data,
933                           guint     *out_num_elements)
934 {
935   GDBusMethodInfo **ret;
936   if (out_num_elements != NULL)
937     *out_num_elements = data->methods->len;
938   if (data->methods == NULL)
939     ret = NULL;
940   else
941     {
942       g_ptr_array_add (data->methods, NULL);
943       ret = (GDBusMethodInfo **) g_ptr_array_free (data->methods, FALSE);
944     }
945   data->methods = g_ptr_array_new ();
946   return ret;
947 }
948 
949 static GDBusSignalInfo **
parse_data_steal_signals(ParseData * data,guint * out_num_elements)950 parse_data_steal_signals (ParseData *data,
951                           guint     *out_num_elements)
952 {
953   GDBusSignalInfo **ret;
954   if (out_num_elements != NULL)
955     *out_num_elements = data->signals->len;
956   if (data->signals == NULL)
957     ret = NULL;
958   else
959     {
960       g_ptr_array_add (data->signals, NULL);
961       ret = (GDBusSignalInfo **) g_ptr_array_free (data->signals, FALSE);
962     }
963   data->signals = g_ptr_array_new ();
964   return ret;
965 }
966 
967 static GDBusPropertyInfo **
parse_data_steal_properties(ParseData * data,guint * out_num_elements)968 parse_data_steal_properties (ParseData *data,
969                              guint     *out_num_elements)
970 {
971   GDBusPropertyInfo **ret;
972   if (out_num_elements != NULL)
973     *out_num_elements = data->properties->len;
974   if (data->properties == NULL)
975     ret = NULL;
976   else
977     {
978       g_ptr_array_add (data->properties, NULL);
979       ret = (GDBusPropertyInfo **) g_ptr_array_free (data->properties, FALSE);
980     }
981   data->properties = g_ptr_array_new ();
982   return ret;
983 }
984 
985 static GDBusInterfaceInfo **
parse_data_steal_interfaces(ParseData * data,guint * out_num_elements)986 parse_data_steal_interfaces (ParseData *data,
987                              guint     *out_num_elements)
988 {
989   GDBusInterfaceInfo **ret;
990   if (out_num_elements != NULL)
991     *out_num_elements = data->interfaces->len;
992   if (data->interfaces == NULL)
993     ret = NULL;
994   else
995     {
996       g_ptr_array_add (data->interfaces, NULL);
997       ret = (GDBusInterfaceInfo **) g_ptr_array_free (data->interfaces, FALSE);
998     }
999   data->interfaces = g_ptr_array_new ();
1000   return ret;
1001 }
1002 
1003 static GDBusNodeInfo **
parse_data_steal_nodes(ParseData * data,guint * out_num_elements)1004 parse_data_steal_nodes (ParseData *data,
1005                         guint     *out_num_elements)
1006 {
1007   GDBusNodeInfo **ret;
1008   if (out_num_elements != NULL)
1009     *out_num_elements = data->nodes->len;
1010   if (data->nodes == NULL)
1011     ret = NULL;
1012   else
1013     {
1014       g_ptr_array_add (data->nodes, NULL);
1015       ret = (GDBusNodeInfo **) g_ptr_array_free (data->nodes, FALSE);
1016     }
1017   data->nodes = g_ptr_array_new ();
1018   return ret;
1019 }
1020 
1021 /* ---------------------------------------------------------------------------------------------------- */
1022 
1023 static void
parse_data_free_annotations(ParseData * data)1024 parse_data_free_annotations (ParseData *data)
1025 {
1026   if (data->annotations == NULL)
1027     return;
1028   g_ptr_array_foreach (data->annotations, (GFunc) g_dbus_annotation_info_unref, NULL);
1029   g_ptr_array_free (data->annotations, TRUE);
1030   data->annotations = NULL;
1031 }
1032 
1033 static void
parse_data_free_args(ParseData * data)1034 parse_data_free_args (ParseData *data)
1035 {
1036   if (data->args == NULL)
1037     return;
1038   g_ptr_array_foreach (data->args, (GFunc) g_dbus_arg_info_unref, NULL);
1039   g_ptr_array_free (data->args, TRUE);
1040   data->args = NULL;
1041 }
1042 
1043 static void
parse_data_free_out_args(ParseData * data)1044 parse_data_free_out_args (ParseData *data)
1045 {
1046   if (data->out_args == NULL)
1047     return;
1048   g_ptr_array_foreach (data->out_args, (GFunc) g_dbus_arg_info_unref, NULL);
1049   g_ptr_array_free (data->out_args, TRUE);
1050   data->out_args = NULL;
1051 }
1052 
1053 static void
parse_data_free_methods(ParseData * data)1054 parse_data_free_methods (ParseData *data)
1055 {
1056   if (data->methods == NULL)
1057     return;
1058   g_ptr_array_foreach (data->methods, (GFunc) g_dbus_method_info_unref, NULL);
1059   g_ptr_array_free (data->methods, TRUE);
1060   data->methods = NULL;
1061 }
1062 
1063 static void
parse_data_free_signals(ParseData * data)1064 parse_data_free_signals (ParseData *data)
1065 {
1066   if (data->signals == NULL)
1067     return;
1068   g_ptr_array_foreach (data->signals, (GFunc) g_dbus_signal_info_unref, NULL);
1069   g_ptr_array_free (data->signals, TRUE);
1070   data->signals = NULL;
1071 }
1072 
1073 static void
parse_data_free_properties(ParseData * data)1074 parse_data_free_properties (ParseData *data)
1075 {
1076   if (data->properties == NULL)
1077     return;
1078   g_ptr_array_foreach (data->properties, (GFunc) g_dbus_property_info_unref, NULL);
1079   g_ptr_array_free (data->properties, TRUE);
1080   data->properties = NULL;
1081 }
1082 
1083 static void
parse_data_free_interfaces(ParseData * data)1084 parse_data_free_interfaces (ParseData *data)
1085 {
1086   if (data->interfaces == NULL)
1087     return;
1088   g_ptr_array_foreach (data->interfaces, (GFunc) g_dbus_interface_info_unref, NULL);
1089   g_ptr_array_free (data->interfaces, TRUE);
1090   data->interfaces = NULL;
1091 }
1092 
1093 static void
parse_data_free_nodes(ParseData * data)1094 parse_data_free_nodes (ParseData *data)
1095 {
1096   if (data->nodes == NULL)
1097     return;
1098   g_ptr_array_foreach (data->nodes, (GFunc) g_dbus_node_info_unref, NULL);
1099   g_ptr_array_free (data->nodes, TRUE);
1100   data->nodes = NULL;
1101 }
1102 
1103 /* ---------------------------------------------------------------------------------------------------- */
1104 
1105 static GDBusAnnotationInfo *
parse_data_get_annotation(ParseData * data,gboolean create_new)1106 parse_data_get_annotation (ParseData *data,
1107                            gboolean   create_new)
1108 {
1109   if (create_new)
1110     g_ptr_array_add (data->annotations, g_new0 (GDBusAnnotationInfo, 1));
1111   return data->annotations->pdata[data->annotations->len - 1];
1112 }
1113 
1114 static GDBusArgInfo *
parse_data_get_arg(ParseData * data,gboolean create_new)1115 parse_data_get_arg (ParseData *data,
1116                     gboolean   create_new)
1117 {
1118   if (create_new)
1119     g_ptr_array_add (data->args, g_new0 (GDBusArgInfo, 1));
1120   return data->args->pdata[data->args->len - 1];
1121 }
1122 
1123 static GDBusArgInfo *
parse_data_get_out_arg(ParseData * data,gboolean create_new)1124 parse_data_get_out_arg (ParseData *data,
1125                         gboolean   create_new)
1126 {
1127   if (create_new)
1128     g_ptr_array_add (data->out_args, g_new0 (GDBusArgInfo, 1));
1129   return data->out_args->pdata[data->out_args->len - 1];
1130 }
1131 
1132 static GDBusMethodInfo *
parse_data_get_method(ParseData * data,gboolean create_new)1133 parse_data_get_method (ParseData *data,
1134                        gboolean   create_new)
1135 {
1136   if (create_new)
1137     g_ptr_array_add (data->methods, g_new0 (GDBusMethodInfo, 1));
1138   return data->methods->pdata[data->methods->len - 1];
1139 }
1140 
1141 static GDBusSignalInfo *
parse_data_get_signal(ParseData * data,gboolean create_new)1142 parse_data_get_signal (ParseData *data,
1143                        gboolean   create_new)
1144 {
1145   if (create_new)
1146     g_ptr_array_add (data->signals, g_new0 (GDBusSignalInfo, 1));
1147   return data->signals->pdata[data->signals->len - 1];
1148 }
1149 
1150 static GDBusPropertyInfo *
parse_data_get_property(ParseData * data,gboolean create_new)1151 parse_data_get_property (ParseData *data,
1152                          gboolean   create_new)
1153 {
1154   if (create_new)
1155     g_ptr_array_add (data->properties, g_new0 (GDBusPropertyInfo, 1));
1156   return data->properties->pdata[data->properties->len - 1];
1157 }
1158 
1159 static GDBusInterfaceInfo *
parse_data_get_interface(ParseData * data,gboolean create_new)1160 parse_data_get_interface (ParseData *data,
1161                           gboolean   create_new)
1162 {
1163   if (create_new)
1164     g_ptr_array_add (data->interfaces, g_new0 (GDBusInterfaceInfo, 1));
1165   return data->interfaces->pdata[data->interfaces->len - 1];
1166 }
1167 
1168 static GDBusNodeInfo *
parse_data_get_node(ParseData * data,gboolean create_new)1169 parse_data_get_node (ParseData *data,
1170                      gboolean   create_new)
1171 {
1172   if (create_new)
1173     g_ptr_array_add (data->nodes, g_new0 (GDBusNodeInfo, 1));
1174   return data->nodes->pdata[data->nodes->len - 1];
1175 }
1176 
1177 /* ---------------------------------------------------------------------------------------------------- */
1178 
1179 static ParseData *
parse_data_new(void)1180 parse_data_new (void)
1181 {
1182   ParseData *data;
1183 
1184   data = g_new0 (ParseData, 1);
1185 
1186   /* initialize arrays */
1187   parse_data_steal_annotations (data, NULL);
1188   parse_data_steal_args (data, NULL);
1189   parse_data_steal_out_args (data, NULL);
1190   parse_data_steal_methods (data, NULL);
1191   parse_data_steal_signals (data, NULL);
1192   parse_data_steal_properties (data, NULL);
1193   parse_data_steal_interfaces (data, NULL);
1194   parse_data_steal_nodes (data, NULL);
1195 
1196   return data;
1197 }
1198 
1199 static void
parse_data_free(ParseData * data)1200 parse_data_free (ParseData *data)
1201 {
1202   GSList *l;
1203 
1204   /* free stack of annotation arrays */
1205   for (l = data->annotations_stack; l != NULL; l = l->next)
1206     {
1207       GPtrArray *annotations = l->data;
1208       g_ptr_array_foreach (annotations, (GFunc) g_dbus_annotation_info_unref, NULL);
1209       g_ptr_array_free (annotations, TRUE);
1210     }
1211   g_slist_free (data->annotations_stack);
1212 
1213   /* free stack of interface arrays */
1214   for (l = data->interfaces_stack; l != NULL; l = l->next)
1215     {
1216       GPtrArray *interfaces = l->data;
1217       g_ptr_array_foreach (interfaces, (GFunc) g_dbus_interface_info_unref, NULL);
1218       g_ptr_array_free (interfaces, TRUE);
1219     }
1220   g_slist_free (data->interfaces_stack);
1221 
1222   /* free stack of node arrays */
1223   for (l = data->nodes_stack; l != NULL; l = l->next)
1224     {
1225       GPtrArray *nodes = l->data;
1226       g_ptr_array_foreach (nodes, (GFunc) g_dbus_node_info_unref, NULL);
1227       g_ptr_array_free (nodes, TRUE);
1228     }
1229   g_slist_free (data->nodes_stack);
1230 
1231   /* free arrays (data->annotations, data->interfaces and data->nodes have been freed above) */
1232   parse_data_free_args (data);
1233   parse_data_free_out_args (data);
1234   parse_data_free_methods (data);
1235   parse_data_free_signals (data);
1236   parse_data_free_properties (data);
1237   parse_data_free_interfaces (data);
1238   parse_data_free_annotations (data);
1239   parse_data_free_nodes (data);
1240 
1241   g_free (data);
1242 }
1243 
1244 /* ---------------------------------------------------------------------------------------------------- */
1245 
1246 static void
parser_start_element(GMarkupParseContext * context,const gchar * element_name,const gchar ** attribute_names,const gchar ** attribute_values,gpointer user_data,GError ** error)1247 parser_start_element (GMarkupParseContext  *context,
1248                       const gchar          *element_name,
1249                       const gchar         **attribute_names,
1250                       const gchar         **attribute_values,
1251                       gpointer              user_data,
1252                       GError              **error)
1253 {
1254   ParseData *data = user_data;
1255   GSList *stack;
1256   const gchar *name;
1257   const gchar *type;
1258   const gchar *access;
1259   const gchar *direction;
1260   const gchar *value;
1261 
1262   name = NULL;
1263   type = NULL;
1264   access = NULL;
1265   direction = NULL;
1266   value = NULL;
1267 
1268   stack = (GSList *) g_markup_parse_context_get_element_stack (context);
1269 
1270   /* ---------------------------------------------------------------------------------------------------- */
1271   if (strcmp (element_name, "node") == 0)
1272     {
1273       if (!(g_slist_length (stack) >= 1 || strcmp (stack->next->data, "node") != 0))
1274         {
1275           g_set_error_literal (error,
1276                                G_MARKUP_ERROR,
1277                                G_MARKUP_ERROR_INVALID_CONTENT,
1278                                "<node> elements can only be top-level or embedded in other <node> elements");
1279           goto out;
1280         }
1281 
1282       if (!g_markup_collect_attributes (element_name,
1283                                         attribute_names,
1284                                         attribute_values,
1285                                         error,
1286                                         G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL, "name", &name,
1287                                         /* some hand-written introspection XML documents use this */
1288                                         G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL, "xmlns:doc", NULL,
1289                                         G_MARKUP_COLLECT_INVALID))
1290         goto out;
1291 
1292       g_dbus_node_info_set (data,
1293                             parse_data_get_node (data, TRUE),
1294                             name,
1295                             NULL,
1296                             NULL,
1297                             NULL);
1298 
1299       /* push the currently retrieved interfaces and nodes on the stack and prepare new arrays */
1300       data->interfaces_stack = g_slist_prepend (data->interfaces_stack, data->interfaces);
1301       data->interfaces = NULL;
1302       parse_data_steal_interfaces (data, NULL);
1303 
1304       data->nodes_stack = g_slist_prepend (data->nodes_stack, data->nodes);
1305       data->nodes = NULL;
1306       parse_data_steal_nodes (data, NULL);
1307 
1308     }
1309   /* ---------------------------------------------------------------------------------------------------- */
1310   else if (strcmp (element_name, "interface") == 0)
1311     {
1312       if (g_slist_length (stack) < 2 || strcmp (stack->next->data, "node") != 0)
1313         {
1314           g_set_error_literal (error,
1315                                G_MARKUP_ERROR,
1316                                G_MARKUP_ERROR_INVALID_CONTENT,
1317                                "<interface> elements can only be embedded in <node> elements");
1318           goto out;
1319         }
1320 
1321       if (!g_markup_collect_attributes (element_name,
1322                                         attribute_names,
1323                                         attribute_values,
1324                                         error,
1325                                         G_MARKUP_COLLECT_STRING, "name", &name,
1326                                         /* seen in the wild */
1327                                         G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL, "version", NULL,
1328                                         G_MARKUP_COLLECT_INVALID))
1329         goto out;
1330 
1331       g_dbus_interface_info_set (data,
1332                                  parse_data_get_interface (data, TRUE),
1333                                  name,
1334                                  NULL,
1335                                  NULL,
1336                                  NULL,
1337                                  NULL);
1338 
1339     }
1340   /* ---------------------------------------------------------------------------------------------------- */
1341   else if (strcmp (element_name, "method") == 0)
1342     {
1343       if (g_slist_length (stack) < 2 || strcmp (stack->next->data, "interface") != 0)
1344         {
1345           g_set_error_literal (error,
1346                                G_MARKUP_ERROR,
1347                                G_MARKUP_ERROR_INVALID_CONTENT,
1348                                "<method> elements can only be embedded in <interface> elements");
1349           goto out;
1350         }
1351 
1352       if (!g_markup_collect_attributes (element_name,
1353                                         attribute_names,
1354                                         attribute_values,
1355                                         error,
1356                                         G_MARKUP_COLLECT_STRING, "name", &name,
1357                                         /* seen in the wild */
1358                                         G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL, "version", NULL,
1359                                         G_MARKUP_COLLECT_INVALID))
1360         goto out;
1361 
1362       g_dbus_method_info_set (data,
1363                               parse_data_get_method (data, TRUE),
1364                               name,
1365                               NULL,
1366                               NULL,
1367                               NULL);
1368 
1369       data->num_args = 0;
1370 
1371     }
1372   /* ---------------------------------------------------------------------------------------------------- */
1373   else if (strcmp (element_name, "signal") == 0)
1374     {
1375       if (g_slist_length (stack) < 2 || strcmp (stack->next->data, "interface") != 0)
1376         {
1377           g_set_error_literal (error,
1378                                G_MARKUP_ERROR,
1379                                G_MARKUP_ERROR_INVALID_CONTENT,
1380                                "<signal> elements can only be embedded in <interface> elements");
1381           goto out;
1382         }
1383 
1384       if (!g_markup_collect_attributes (element_name,
1385                                         attribute_names,
1386                                         attribute_values,
1387                                         error,
1388                                         G_MARKUP_COLLECT_STRING, "name", &name,
1389                                         G_MARKUP_COLLECT_INVALID))
1390         goto out;
1391 
1392       g_dbus_signal_info_set (data,
1393                               parse_data_get_signal (data, TRUE),
1394                               name,
1395                               NULL,
1396                               NULL);
1397 
1398       data->num_args = 0;
1399 
1400     }
1401   /* ---------------------------------------------------------------------------------------------------- */
1402   else if (strcmp (element_name, "property") == 0)
1403     {
1404       GDBusPropertyInfoFlags flags;
1405 
1406       if (g_slist_length (stack) < 2 || strcmp (stack->next->data, "interface") != 0)
1407         {
1408           g_set_error_literal (error,
1409                                G_MARKUP_ERROR,
1410                                G_MARKUP_ERROR_INVALID_CONTENT,
1411                                "<property> elements can only be embedded in <interface> elements");
1412           goto out;
1413         }
1414 
1415       if (!g_markup_collect_attributes (element_name,
1416                                         attribute_names,
1417                                         attribute_values,
1418                                         error,
1419                                         G_MARKUP_COLLECT_STRING, "name", &name,
1420                                         G_MARKUP_COLLECT_STRING, "type", &type,
1421                                         G_MARKUP_COLLECT_STRING, "access", &access,
1422                                         G_MARKUP_COLLECT_INVALID))
1423         goto out;
1424 
1425       if (strcmp (access, "read") == 0)
1426         flags = G_DBUS_PROPERTY_INFO_FLAGS_READABLE;
1427       else if (strcmp (access, "write") == 0)
1428         flags = G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE;
1429       else if (strcmp (access, "readwrite") == 0)
1430         flags = G_DBUS_PROPERTY_INFO_FLAGS_READABLE | G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE;
1431       else
1432         {
1433           g_set_error (error,
1434                        G_MARKUP_ERROR,
1435                        G_MARKUP_ERROR_INVALID_CONTENT,
1436                        "Unknown value '%s' of access attribute for element <property>",
1437                        access);
1438           goto out;
1439         }
1440 
1441       g_dbus_property_info_set (data,
1442                                 parse_data_get_property (data, TRUE),
1443                                 name,
1444                                 type,
1445                                 flags,
1446                                 NULL);
1447 
1448     }
1449   /* ---------------------------------------------------------------------------------------------------- */
1450   else if (strcmp (element_name, "arg") == 0)
1451     {
1452       gboolean is_in;
1453       gchar *name_to_use;
1454 
1455       if (g_slist_length (stack) < 2 ||
1456           (strcmp (stack->next->data, "method") != 0 &&
1457            strcmp (stack->next->data, "signal") != 0))
1458         {
1459           g_set_error_literal (error,
1460                                G_MARKUP_ERROR,
1461                                G_MARKUP_ERROR_INVALID_CONTENT,
1462                                "<arg> elements can only be embedded in <method> or <signal> elements");
1463           goto out;
1464         }
1465 
1466       if (!g_markup_collect_attributes (element_name,
1467                                         attribute_names,
1468                                         attribute_values,
1469                                         error,
1470                                         G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL, "name", &name,
1471                                         G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL, "direction", &direction,
1472                                         G_MARKUP_COLLECT_STRING, "type", &type,
1473                                         G_MARKUP_COLLECT_INVALID))
1474         goto out;
1475 
1476       if (strcmp (stack->next->data, "method") == 0)
1477         is_in = TRUE;
1478       else
1479         is_in = FALSE;
1480       if (direction != NULL)
1481         {
1482           if (strcmp (direction, "in") == 0)
1483             is_in = TRUE;
1484           else if (strcmp (direction, "out") == 0)
1485             is_in = FALSE;
1486           else
1487             {
1488               g_set_error (error,
1489                            G_MARKUP_ERROR,
1490                            G_MARKUP_ERROR_INVALID_CONTENT,
1491                            "Unknown value '%s' of direction attribute",
1492                            direction);
1493               goto out;
1494             }
1495         }
1496 
1497       if (is_in && strcmp (stack->next->data, "signal") == 0)
1498         {
1499           g_set_error_literal (error,
1500                                G_MARKUP_ERROR,
1501                                G_MARKUP_ERROR_INVALID_CONTENT,
1502                                "Only direction 'out' is allowed for <arg> elements embedded in <signal>");
1503           goto out;
1504         }
1505 
1506       if (name == NULL)
1507         name_to_use = g_strdup_printf ("arg_%d", data->num_args);
1508       else
1509         name_to_use = g_strdup (name);
1510       data->num_args++;
1511 
1512       if (is_in)
1513         {
1514           g_dbus_arg_info_set (data,
1515                                parse_data_get_arg (data, TRUE),
1516                                name_to_use,
1517                                type,
1518                                NULL);
1519           data->last_arg_was_in = TRUE;
1520         }
1521       else
1522         {
1523           g_dbus_arg_info_set (data,
1524                                parse_data_get_out_arg (data, TRUE),
1525                                name_to_use,
1526                                type,
1527                                NULL);
1528           data->last_arg_was_in = FALSE;
1529 
1530         }
1531 
1532       g_free (name_to_use);
1533     }
1534   /* ---------------------------------------------------------------------------------------------------- */
1535   else if (strcmp (element_name, "annotation") == 0)
1536     {
1537       if (g_slist_length (stack) < 2 ||
1538           (strcmp (stack->next->data, "node") != 0 &&
1539            strcmp (stack->next->data, "interface") != 0 &&
1540            strcmp (stack->next->data, "signal") != 0 &&
1541            strcmp (stack->next->data, "method") != 0 &&
1542            strcmp (stack->next->data, "property") != 0 &&
1543            strcmp (stack->next->data, "arg") != 0 &&
1544            strcmp (stack->next->data, "annotation") != 0))
1545         {
1546           g_set_error_literal (error,
1547                                G_MARKUP_ERROR,
1548                                G_MARKUP_ERROR_INVALID_CONTENT,
1549                                "<annotation> elements can only be embedded in <node>, <interface>, <signal>, <method>, <property>, <arg> or <annotation> elements");
1550           goto out;
1551         }
1552 
1553       if (!g_markup_collect_attributes (element_name,
1554                                         attribute_names,
1555                                         attribute_values,
1556                                         error,
1557                                         G_MARKUP_COLLECT_STRING, "name", &name,
1558                                         G_MARKUP_COLLECT_STRING, "value", &value,
1559                                         G_MARKUP_COLLECT_INVALID))
1560         goto out;
1561 
1562       g_dbus_annotation_info_set (data,
1563                                   parse_data_get_annotation (data, TRUE),
1564                                   name,
1565                                   value,
1566                                   NULL);
1567     }
1568   /* ---------------------------------------------------------------------------------------------------- */
1569   else
1570     {
1571       /* don't bail on unknown elements; just ignore them */
1572     }
1573   /* ---------------------------------------------------------------------------------------------------- */
1574 
1575   /* push the currently retrieved annotations on the stack and prepare a new one */
1576   data->annotations_stack = g_slist_prepend (data->annotations_stack, data->annotations);
1577   data->annotations = NULL;
1578   parse_data_steal_annotations (data, NULL);
1579 
1580  out:
1581   ;
1582 }
1583 
1584 /* ---------------------------------------------------------------------------------------------------- */
1585 
1586 static GDBusAnnotationInfo **
steal_annotations(ParseData * data)1587 steal_annotations (ParseData *data)
1588 {
1589   return parse_data_steal_annotations (data, NULL);
1590 }
1591 
1592 
1593 static void
parser_end_element(GMarkupParseContext * context,const gchar * element_name,gpointer user_data,GError ** error)1594 parser_end_element (GMarkupParseContext  *context,
1595                     const gchar          *element_name,
1596                     gpointer              user_data,
1597                     GError              **error)
1598 {
1599   ParseData *data = user_data;
1600   gboolean have_popped_annotations;
1601 
1602   have_popped_annotations = FALSE;
1603 
1604   if (strcmp (element_name, "node") == 0)
1605     {
1606       guint num_nodes;
1607       guint num_interfaces;
1608       GDBusNodeInfo **nodes;
1609       GDBusInterfaceInfo **interfaces;
1610 
1611       nodes = parse_data_steal_nodes (data, &num_nodes);
1612       interfaces = parse_data_steal_interfaces (data, &num_interfaces);
1613 
1614       /* destroy the nodes, interfaces for scope we're exiting and and pop the nodes, interfaces from the
1615        * scope we're reentering
1616        */
1617       parse_data_free_interfaces (data);
1618       data->interfaces = (GPtrArray *) data->interfaces_stack->data;
1619       data->interfaces_stack = g_slist_remove (data->interfaces_stack, data->interfaces_stack->data);
1620 
1621       parse_data_free_nodes (data);
1622       data->nodes = (GPtrArray *) data->nodes_stack->data;
1623       data->nodes_stack = g_slist_remove (data->nodes_stack, data->nodes_stack->data);
1624 
1625       g_dbus_node_info_set (data,
1626                             parse_data_get_node (data, FALSE),
1627                             NULL,
1628                             interfaces,
1629                             nodes,
1630                             steal_annotations (data));
1631 
1632     }
1633   else if (strcmp (element_name, "interface") == 0)
1634     {
1635       guint num_methods;
1636       guint num_signals;
1637       guint num_properties;
1638       GDBusMethodInfo **methods;
1639       GDBusSignalInfo **signals;
1640       GDBusPropertyInfo **properties;
1641 
1642       methods    = parse_data_steal_methods    (data, &num_methods);
1643       signals    = parse_data_steal_signals    (data, &num_signals);
1644       properties = parse_data_steal_properties (data, &num_properties);
1645 
1646       g_dbus_interface_info_set (data,
1647                                  parse_data_get_interface (data, FALSE),
1648                                  NULL,
1649                                  methods,
1650                                  signals,
1651                                  properties,
1652                                  steal_annotations (data));
1653 
1654     }
1655   else if (strcmp (element_name, "method") == 0)
1656     {
1657       guint in_num_args;
1658       guint out_num_args;
1659       GDBusArgInfo **in_args;
1660       GDBusArgInfo **out_args;
1661 
1662       in_args  = parse_data_steal_args     (data, &in_num_args);
1663       out_args = parse_data_steal_out_args (data, &out_num_args);
1664 
1665       g_dbus_method_info_set (data,
1666                               parse_data_get_method (data, FALSE),
1667                               NULL,
1668                               in_args,
1669                               out_args,
1670                               steal_annotations (data));
1671     }
1672   else if (strcmp (element_name, "signal") == 0)
1673     {
1674       guint num_args;
1675       GDBusArgInfo **args;
1676 
1677       args = parse_data_steal_out_args (data, &num_args);
1678 
1679       g_dbus_signal_info_set (data,
1680                               parse_data_get_signal (data, FALSE),
1681                               NULL,
1682                               args,
1683                               steal_annotations (data));
1684     }
1685   else if (strcmp (element_name, "property") == 0)
1686     {
1687       g_dbus_property_info_set (data,
1688                                 parse_data_get_property (data, FALSE),
1689                                 NULL,
1690                                 NULL,
1691                                 G_DBUS_PROPERTY_INFO_FLAGS_NONE,
1692                                 steal_annotations (data));
1693     }
1694   else if (strcmp (element_name, "arg") == 0)
1695     {
1696       g_dbus_arg_info_set (data,
1697                            data->last_arg_was_in ? parse_data_get_arg (data, FALSE) : parse_data_get_out_arg (data, FALSE),
1698                            NULL,
1699                            NULL,
1700                            steal_annotations (data));
1701     }
1702   else if (strcmp (element_name, "annotation") == 0)
1703     {
1704       GDBusAnnotationInfo **embedded_annotations;
1705 
1706       embedded_annotations = steal_annotations (data);
1707 
1708       /* destroy the annotations for scope we're exiting and and pop the annotations from the scope we're reentering */
1709       parse_data_free_annotations (data);
1710       data->annotations = (GPtrArray *) data->annotations_stack->data;
1711       data->annotations_stack = g_slist_remove (data->annotations_stack, data->annotations_stack->data);
1712 
1713       have_popped_annotations = TRUE;
1714 
1715       g_dbus_annotation_info_set (data,
1716                                   parse_data_get_annotation (data, FALSE),
1717                                   NULL,
1718                                   NULL,
1719                                   embedded_annotations);
1720     }
1721   else
1722     {
1723       /* don't bail on unknown elements; just ignore them */
1724     }
1725 
1726   if (!have_popped_annotations)
1727     {
1728       /* destroy the annotations for scope we're exiting and and pop the annotations from the scope we're reentering */
1729       parse_data_free_annotations (data);
1730       data->annotations = (GPtrArray *) data->annotations_stack->data;
1731       data->annotations_stack = g_slist_remove (data->annotations_stack, data->annotations_stack->data);
1732     }
1733 }
1734 
1735 /* ---------------------------------------------------------------------------------------------------- */
1736 
1737 static void
parser_error(GMarkupParseContext * context,GError * error,gpointer user_data)1738 parser_error (GMarkupParseContext *context,
1739               GError              *error,
1740               gpointer             user_data)
1741 {
1742   gint line_number;
1743   gint char_number;
1744 
1745   g_markup_parse_context_get_position (context, &line_number, &char_number);
1746 
1747   g_prefix_error (&error, "%d:%d: ",
1748                   line_number,
1749                   char_number);
1750 }
1751 
1752 /* ---------------------------------------------------------------------------------------------------- */
1753 
1754 /**
1755  * g_dbus_node_info_new_for_xml:
1756  * @xml_data: Valid D-Bus introspection XML.
1757  * @error: Return location for error.
1758  *
1759  * Parses @xml_data and returns a #GDBusNodeInfo representing the data.
1760  *
1761  * The introspection XML must contain exactly one top-level
1762  * <node> element.
1763  *
1764  * Note that this routine is using a
1765  * [GMarkup][glib-Simple-XML-Subset-Parser.description]-based
1766  * parser that only accepts a subset of valid XML documents.
1767  *
1768  * Returns: A #GDBusNodeInfo structure or %NULL if @error is set. Free
1769  * with g_dbus_node_info_unref().
1770  *
1771  * Since: 2.26
1772  */
1773 GDBusNodeInfo *
g_dbus_node_info_new_for_xml(const gchar * xml_data,GError ** error)1774 g_dbus_node_info_new_for_xml (const gchar  *xml_data,
1775                               GError      **error)
1776 {
1777   GDBusNodeInfo *ret;
1778   GMarkupParseContext *context;
1779   GMarkupParser *parser;
1780   guint num_nodes;
1781   ParseData *data;
1782   GDBusNodeInfo **ughret;
1783 
1784   ret = NULL;
1785   parser = NULL;
1786   context = NULL;
1787 
1788   parser = g_new0 (GMarkupParser, 1);
1789   parser->start_element = parser_start_element;
1790   parser->end_element   = parser_end_element;
1791   parser->error         = parser_error;
1792 
1793   data = parse_data_new ();
1794   context = g_markup_parse_context_new (parser,
1795                                         G_MARKUP_IGNORE_QUALIFIED,
1796                                         data,
1797                                         (GDestroyNotify) parse_data_free);
1798 
1799   if (!g_markup_parse_context_parse (context,
1800                                      xml_data,
1801                                      strlen (xml_data),
1802                                      error))
1803     goto out;
1804 
1805   if (!g_markup_parse_context_end_parse (context, error))
1806     goto out;
1807 
1808   ughret = parse_data_steal_nodes (data, &num_nodes);
1809 
1810   if (num_nodes != 1)
1811     {
1812       guint n;
1813 
1814       g_set_error (error,
1815                    G_MARKUP_ERROR,
1816                    G_MARKUP_ERROR_INVALID_CONTENT,
1817                    "Expected a single node in introspection XML, found %d",
1818                    num_nodes);
1819 
1820       /* clean up */
1821       for (n = 0; n < num_nodes; n++)
1822         {
1823           g_dbus_node_info_unref (ughret[n]);
1824           ughret[n] = NULL;
1825         }
1826     }
1827 
1828   ret = ughret[0];
1829   g_free (ughret);
1830 
1831  out:
1832   g_free (parser);
1833   if (context != NULL)
1834     g_markup_parse_context_free (context);
1835 
1836   return ret;
1837 }
1838 
1839 /* ---------------------------------------------------------------------------------------------------- */
1840 
1841 /**
1842  * g_dbus_annotation_info_lookup:
1843  * @annotations: (array zero-terminated=1) (nullable): A %NULL-terminated array of annotations or %NULL.
1844  * @name: The name of the annotation to look up.
1845  *
1846  * Looks up the value of an annotation.
1847  *
1848  * The cost of this function is O(n) in number of annotations.
1849  *
1850  * Returns: (nullable): The value or %NULL if not found. Do not free, it is owned by @annotations.
1851  *
1852  * Since: 2.26
1853  */
1854 const gchar *
g_dbus_annotation_info_lookup(GDBusAnnotationInfo ** annotations,const gchar * name)1855 g_dbus_annotation_info_lookup (GDBusAnnotationInfo **annotations,
1856                                const gchar          *name)
1857 {
1858   guint n;
1859   const gchar *ret;
1860 
1861   ret = NULL;
1862   for (n = 0; annotations != NULL && annotations[n] != NULL; n++)
1863     {
1864       if (g_strcmp0 (annotations[n]->key, name) == 0)
1865         {
1866           ret = annotations[n]->value;
1867           goto out;
1868         }
1869     }
1870 
1871  out:
1872   return ret;
1873 }
1874 
1875 /* ---------------------------------------------------------------------------------------------------- */
1876 
1877 G_LOCK_DEFINE_STATIC (info_cache_lock);
1878 
1879 typedef struct
1880 {
1881   gint use_count;
1882 
1883   /* gchar* -> GDBusMethodInfo* */
1884   GHashTable *method_name_to_data;
1885 
1886   /* gchar* -> GDBusMethodInfo* */
1887   GHashTable *signal_name_to_data;
1888 
1889   /* gchar* -> GDBusMethodInfo* */
1890   GHashTable *property_name_to_data;
1891 } InfoCacheEntry;
1892 
1893 static void
info_cache_free(InfoCacheEntry * cache)1894 info_cache_free (InfoCacheEntry *cache)
1895 {
1896   g_assert (cache->use_count == 0);
1897   g_hash_table_unref (cache->method_name_to_data);
1898   g_hash_table_unref (cache->signal_name_to_data);
1899   g_hash_table_unref (cache->property_name_to_data);
1900   g_slice_free (InfoCacheEntry, cache);
1901 }
1902 
1903 /* maps from GDBusInterfaceInfo* to InfoCacheEntry* */
1904 static GHashTable *info_cache = NULL;
1905 
1906 /* ---------------------------------------------------------------------------------------------------- */
1907 
1908 /**
1909  * g_dbus_interface_info_lookup_method:
1910  * @info: A #GDBusInterfaceInfo.
1911  * @name: A D-Bus method name (typically in CamelCase)
1912  *
1913  * Looks up information about a method.
1914  *
1915  * The cost of this function is O(n) in number of methods unless
1916  * g_dbus_interface_info_cache_build() has been used on @info.
1917  *
1918  * Returns: (nullable) (transfer none): A #GDBusMethodInfo or %NULL if not found. Do not free, it is owned by @info.
1919  *
1920  * Since: 2.26
1921  */
1922 GDBusMethodInfo *
g_dbus_interface_info_lookup_method(GDBusInterfaceInfo * info,const gchar * name)1923 g_dbus_interface_info_lookup_method (GDBusInterfaceInfo *info,
1924                                      const gchar        *name)
1925 {
1926   guint n;
1927   GDBusMethodInfo *result;
1928 
1929   G_LOCK (info_cache_lock);
1930   if (G_LIKELY (info_cache != NULL))
1931     {
1932       InfoCacheEntry *cache;
1933       cache = g_hash_table_lookup (info_cache, info);
1934       if (G_LIKELY (cache != NULL))
1935         {
1936           result = g_hash_table_lookup (cache->method_name_to_data, name);
1937           G_UNLOCK (info_cache_lock);
1938           goto out;
1939         }
1940     }
1941   G_UNLOCK (info_cache_lock);
1942 
1943   for (n = 0; info->methods != NULL && info->methods[n] != NULL; n++)
1944     {
1945       GDBusMethodInfo *i = info->methods[n];
1946 
1947       if (g_strcmp0 (i->name, name) == 0)
1948         {
1949           result = i;
1950           goto out;
1951         }
1952     }
1953 
1954   result = NULL;
1955 
1956  out:
1957   return result;
1958 }
1959 
1960 /* ---------------------------------------------------------------------------------------------------- */
1961 
1962 /**
1963  * g_dbus_interface_info_lookup_signal:
1964  * @info: A #GDBusInterfaceInfo.
1965  * @name: A D-Bus signal name (typically in CamelCase)
1966  *
1967  * Looks up information about a signal.
1968  *
1969  * The cost of this function is O(n) in number of signals unless
1970  * g_dbus_interface_info_cache_build() has been used on @info.
1971  *
1972  * Returns: (nullable) (transfer none): A #GDBusSignalInfo or %NULL if not found. Do not free, it is owned by @info.
1973  *
1974  * Since: 2.26
1975  */
1976 GDBusSignalInfo *
g_dbus_interface_info_lookup_signal(GDBusInterfaceInfo * info,const gchar * name)1977 g_dbus_interface_info_lookup_signal (GDBusInterfaceInfo *info,
1978                                      const gchar        *name)
1979 {
1980   guint n;
1981   GDBusSignalInfo *result;
1982 
1983   G_LOCK (info_cache_lock);
1984   if (G_LIKELY (info_cache != NULL))
1985     {
1986       InfoCacheEntry *cache;
1987       cache = g_hash_table_lookup (info_cache, info);
1988       if (G_LIKELY (cache != NULL))
1989         {
1990           result = g_hash_table_lookup (cache->signal_name_to_data, name);
1991           G_UNLOCK (info_cache_lock);
1992           goto out;
1993         }
1994     }
1995   G_UNLOCK (info_cache_lock);
1996 
1997   for (n = 0; info->signals != NULL && info->signals[n] != NULL; n++)
1998     {
1999       GDBusSignalInfo *i = info->signals[n];
2000 
2001       if (g_strcmp0 (i->name, name) == 0)
2002         {
2003           result = i;
2004           goto out;
2005         }
2006     }
2007 
2008   result = NULL;
2009 
2010  out:
2011   return result;
2012 }
2013 
2014 /* ---------------------------------------------------------------------------------------------------- */
2015 
2016 /**
2017  * g_dbus_interface_info_lookup_property:
2018  * @info: A #GDBusInterfaceInfo.
2019  * @name: A D-Bus property name (typically in CamelCase).
2020  *
2021  * Looks up information about a property.
2022  *
2023  * The cost of this function is O(n) in number of properties unless
2024  * g_dbus_interface_info_cache_build() has been used on @info.
2025  *
2026  * Returns: (nullable) (transfer none): A #GDBusPropertyInfo or %NULL if not found. Do not free, it is owned by @info.
2027  *
2028  * Since: 2.26
2029  */
2030 GDBusPropertyInfo *
g_dbus_interface_info_lookup_property(GDBusInterfaceInfo * info,const gchar * name)2031 g_dbus_interface_info_lookup_property (GDBusInterfaceInfo *info,
2032                                        const gchar        *name)
2033 {
2034   guint n;
2035   GDBusPropertyInfo *result;
2036 
2037   G_LOCK (info_cache_lock);
2038   if (G_LIKELY (info_cache != NULL))
2039     {
2040       InfoCacheEntry *cache;
2041       cache = g_hash_table_lookup (info_cache, info);
2042       if (G_LIKELY (cache != NULL))
2043         {
2044           result = g_hash_table_lookup (cache->property_name_to_data, name);
2045           G_UNLOCK (info_cache_lock);
2046           goto out;
2047         }
2048     }
2049   G_UNLOCK (info_cache_lock);
2050 
2051   for (n = 0; info->properties != NULL && info->properties[n] != NULL; n++)
2052     {
2053       GDBusPropertyInfo *i = info->properties[n];
2054 
2055       if (g_strcmp0 (i->name, name) == 0)
2056         {
2057           result = i;
2058           goto out;
2059         }
2060     }
2061 
2062   result = NULL;
2063 
2064  out:
2065   return result;
2066 }
2067 
2068 /* ---------------------------------------------------------------------------------------------------- */
2069 
2070 /**
2071  * g_dbus_interface_info_cache_build:
2072  * @info: A #GDBusInterfaceInfo.
2073  *
2074  * Builds a lookup-cache to speed up
2075  * g_dbus_interface_info_lookup_method(),
2076  * g_dbus_interface_info_lookup_signal() and
2077  * g_dbus_interface_info_lookup_property().
2078  *
2079  * If this has already been called with @info, the existing cache is
2080  * used and its use count is increased.
2081  *
2082  * Note that @info cannot be modified until
2083  * g_dbus_interface_info_cache_release() is called.
2084  *
2085  * Since: 2.30
2086  */
2087 void
g_dbus_interface_info_cache_build(GDBusInterfaceInfo * info)2088 g_dbus_interface_info_cache_build (GDBusInterfaceInfo *info)
2089 {
2090   InfoCacheEntry *cache;
2091   guint n;
2092 
2093   G_LOCK (info_cache_lock);
2094   if (info_cache == NULL)
2095     info_cache = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) info_cache_free);
2096   cache = g_hash_table_lookup (info_cache, info);
2097   if (cache != NULL)
2098     {
2099       cache->use_count += 1;
2100       goto out;
2101     }
2102   cache = g_slice_new0 (InfoCacheEntry);
2103   cache->use_count = 1;
2104   cache->method_name_to_data = g_hash_table_new (g_str_hash, g_str_equal);
2105   cache->signal_name_to_data = g_hash_table_new (g_str_hash, g_str_equal);
2106   cache->property_name_to_data = g_hash_table_new (g_str_hash, g_str_equal);
2107   for (n = 0; info->methods != NULL && info->methods[n] != NULL; n++)
2108     g_hash_table_insert (cache->method_name_to_data, info->methods[n]->name, info->methods[n]);
2109   for (n = 0; info->signals != NULL && info->signals[n] != NULL; n++)
2110     g_hash_table_insert (cache->signal_name_to_data, info->signals[n]->name, info->signals[n]);
2111   for (n = 0; info->properties != NULL && info->properties[n] != NULL; n++)
2112     g_hash_table_insert (cache->property_name_to_data, info->properties[n]->name, info->properties[n]);
2113   g_hash_table_insert (info_cache, info, cache);
2114  out:
2115   G_UNLOCK (info_cache_lock);
2116 }
2117 
2118 /**
2119  * g_dbus_interface_info_cache_release:
2120  * @info: A GDBusInterfaceInfo
2121  *
2122  * Decrements the usage count for the cache for @info built by
2123  * g_dbus_interface_info_cache_build() (if any) and frees the
2124  * resources used by the cache if the usage count drops to zero.
2125  *
2126  * Since: 2.30
2127  */
2128 void
g_dbus_interface_info_cache_release(GDBusInterfaceInfo * info)2129 g_dbus_interface_info_cache_release (GDBusInterfaceInfo *info)
2130 {
2131   InfoCacheEntry *cache;
2132 
2133   G_LOCK (info_cache_lock);
2134   if (G_UNLIKELY (info_cache == NULL))
2135     {
2136       g_warning ("%s called for interface %s but there is no cache", info->name, G_STRFUNC);
2137       goto out;
2138     }
2139 
2140   cache = g_hash_table_lookup (info_cache, info);
2141   if (G_UNLIKELY (cache == NULL))
2142     {
2143       g_warning ("%s called for interface %s but there is no cache entry", info->name, G_STRFUNC);
2144       goto out;
2145     }
2146   cache->use_count -= 1;
2147   if (cache->use_count == 0)
2148     {
2149       g_hash_table_remove (info_cache, info);
2150       /* could nuke info_cache itself if empty */
2151     }
2152  out:
2153   G_UNLOCK (info_cache_lock);
2154 }
2155 
2156 
2157 /* ---------------------------------------------------------------------------------------------------- */
2158 
2159 /**
2160  * g_dbus_node_info_lookup_interface:
2161  * @info: A #GDBusNodeInfo.
2162  * @name: A D-Bus interface name.
2163  *
2164  * Looks up information about an interface.
2165  *
2166  * The cost of this function is O(n) in number of interfaces.
2167  *
2168  * Returns: (nullable) (transfer none): A #GDBusInterfaceInfo or %NULL if not found. Do not free, it is owned by @info.
2169  *
2170  * Since: 2.26
2171  */
2172 GDBusInterfaceInfo *
g_dbus_node_info_lookup_interface(GDBusNodeInfo * info,const gchar * name)2173 g_dbus_node_info_lookup_interface (GDBusNodeInfo *info,
2174                                    const gchar   *name)
2175 {
2176   guint n;
2177   GDBusInterfaceInfo *result;
2178 
2179   for (n = 0; info->interfaces != NULL && info->interfaces[n] != NULL; n++)
2180     {
2181       GDBusInterfaceInfo *i = info->interfaces[n];
2182 
2183       if (g_strcmp0 (i->name, name) == 0)
2184         {
2185           result = i;
2186           goto out;
2187         }
2188     }
2189 
2190   result = NULL;
2191 
2192  out:
2193   return result;
2194 }
2195