Coverage Report

Created: 2025-07-01 07:09

/src/glib/gio/gdbusintrospection.c
Line
Count
Source (jump to first uncovered line)
1
/* GDBus - GLib D-Bus Library
2
 *
3
 * Copyright (C) 2008-2010 Red Hat, Inc.
4
 *
5
 * 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 *
106
g_dbus_node_info_ref (GDBusNodeInfo *info)
107
0
{
108
0
  if (g_atomic_int_get (&info->ref_count) == -1)
109
0
    return info;
110
0
  g_atomic_int_inc (&info->ref_count);
111
0
  return info;
112
0
}
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 *
126
g_dbus_interface_info_ref (GDBusInterfaceInfo *info)
127
0
{
128
0
  if (g_atomic_int_get (&info->ref_count) == -1)
129
0
    return info;
130
0
  g_atomic_int_inc (&info->ref_count);
131
0
  return info;
132
0
}
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 *
146
g_dbus_method_info_ref (GDBusMethodInfo *info)
147
0
{
148
0
  if (g_atomic_int_get (&info->ref_count) == -1)
149
0
    return info;
150
0
  g_atomic_int_inc (&info->ref_count);
151
0
  return info;
152
0
}
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 *
166
g_dbus_signal_info_ref (GDBusSignalInfo *info)
167
0
{
168
0
  if (g_atomic_int_get (&info->ref_count) == -1)
169
0
    return info;
170
0
  g_atomic_int_inc (&info->ref_count);
171
0
  return info;
172
0
}
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 *
186
g_dbus_property_info_ref (GDBusPropertyInfo *info)
187
0
{
188
0
  if (g_atomic_int_get (&info->ref_count) == -1)
189
0
    return info;
190
0
  g_atomic_int_inc (&info->ref_count);
191
0
  return info;
192
0
}
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 *
206
g_dbus_arg_info_ref (GDBusArgInfo *info)
207
0
{
208
0
  if (g_atomic_int_get (&info->ref_count) == -1)
209
0
    return info;
210
0
  g_atomic_int_inc (&info->ref_count);
211
0
  return info;
212
0
}
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 *
226
g_dbus_annotation_info_ref (GDBusAnnotationInfo *info)
227
0
{
228
0
  if (g_atomic_int_get (&info->ref_count) == -1)
229
0
    return info;
230
0
  g_atomic_int_inc (&info->ref_count);
231
0
  return info;
232
0
}
233
234
/* ---------------------------------------------------------------------------------------------------- */
235
236
static void
237
free_null_terminated_array (gpointer array, GDestroyNotify unref_func)
238
0
{
239
0
  guint n;
240
0
  gpointer *p = array;
241
0
  if (p == NULL)
242
0
    return;
243
0
  for (n = 0; p[n] != NULL; n++)
244
0
    unref_func (p[n]);
245
0
  g_free (p);
246
0
}
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
259
g_dbus_annotation_info_unref (GDBusAnnotationInfo *info)
260
0
{
261
0
  if (g_atomic_int_get (&info->ref_count) == -1)
262
0
    return;
263
0
  if (g_atomic_int_dec_and_test (&info->ref_count))
264
0
    {
265
0
      g_free (info->key);
266
0
      g_free (info->value);
267
0
      free_null_terminated_array (info->annotations, (GDestroyNotify) g_dbus_annotation_info_unref);
268
0
      g_free (info);
269
0
    }
270
0
}
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
283
g_dbus_arg_info_unref (GDBusArgInfo *info)
284
0
{
285
0
  if (g_atomic_int_get (&info->ref_count) == -1)
286
0
    return;
287
0
  if (g_atomic_int_dec_and_test (&info->ref_count))
288
0
    {
289
0
      g_free (info->name);
290
0
      g_free (info->signature);
291
0
      free_null_terminated_array (info->annotations, (GDestroyNotify) g_dbus_annotation_info_unref);
292
0
      g_free (info);
293
0
    }
294
0
}
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
307
g_dbus_method_info_unref (GDBusMethodInfo *info)
308
0
{
309
0
  if (g_atomic_int_get (&info->ref_count) == -1)
310
0
    return;
311
0
  if (g_atomic_int_dec_and_test (&info->ref_count))
312
0
    {
313
0
      g_free (info->name);
314
0
      free_null_terminated_array (info->in_args, (GDestroyNotify) g_dbus_arg_info_unref);
315
0
      free_null_terminated_array (info->out_args, (GDestroyNotify) g_dbus_arg_info_unref);
316
0
      free_null_terminated_array (info->annotations, (GDestroyNotify) g_dbus_annotation_info_unref);
317
0
      g_free (info);
318
0
    }
319
0
}
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
332
g_dbus_signal_info_unref (GDBusSignalInfo *info)
333
0
{
334
0
  if (g_atomic_int_get (&info->ref_count) == -1)
335
0
    return;
336
0
  if (g_atomic_int_dec_and_test (&info->ref_count))
337
0
    {
338
0
      g_free (info->name);
339
0
      free_null_terminated_array (info->args, (GDestroyNotify) g_dbus_arg_info_unref);
340
0
      free_null_terminated_array (info->annotations, (GDestroyNotify) g_dbus_annotation_info_unref);
341
0
      g_free (info);
342
0
    }
343
0
}
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
356
g_dbus_property_info_unref (GDBusPropertyInfo *info)
357
0
{
358
0
  if (g_atomic_int_get (&info->ref_count) == -1)
359
0
    return;
360
0
  if (g_atomic_int_dec_and_test (&info->ref_count))
361
0
    {
362
0
      g_free (info->name);
363
0
      g_free (info->signature);
364
0
      free_null_terminated_array (info->annotations, (GDestroyNotify) g_dbus_annotation_info_unref);
365
0
      g_free (info);
366
0
    }
367
0
}
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
380
g_dbus_interface_info_unref (GDBusInterfaceInfo *info)
381
0
{
382
0
  if (g_atomic_int_get (&info->ref_count) == -1)
383
0
    return;
384
0
  if (g_atomic_int_dec_and_test (&info->ref_count))
385
0
    {
386
0
      g_free (info->name);
387
0
      free_null_terminated_array (info->methods, (GDestroyNotify) g_dbus_method_info_unref);
388
0
      free_null_terminated_array (info->signals, (GDestroyNotify) g_dbus_signal_info_unref);
389
0
      free_null_terminated_array (info->properties, (GDestroyNotify) g_dbus_property_info_unref);
390
0
      free_null_terminated_array (info->annotations, (GDestroyNotify) g_dbus_annotation_info_unref);
391
0
      g_free (info);
392
0
    }
393
0
}
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
406
g_dbus_node_info_unref (GDBusNodeInfo *info)
407
0
{
408
0
  if (g_atomic_int_get (&info->ref_count) == -1)
409
0
    return;
410
0
  if (g_atomic_int_dec_and_test (&info->ref_count))
411
0
    {
412
0
      g_free (info->path);
413
0
      free_null_terminated_array (info->interfaces, (GDestroyNotify) g_dbus_interface_info_unref);
414
0
      free_null_terminated_array (info->nodes, (GDestroyNotify) g_dbus_node_info_unref);
415
0
      free_null_terminated_array (info->annotations, (GDestroyNotify) g_dbus_annotation_info_unref);
416
0
      g_free (info);
417
0
    }
418
0
}
419
420
/* ---------------------------------------------------------------------------------------------------- */
421
422
static void
423
g_dbus_annotation_info_set (ParseData            *data,
424
                            GDBusAnnotationInfo  *info,
425
                            const gchar          *key,
426
                            const gchar          *value,
427
                            GDBusAnnotationInfo **embedded_annotations)
428
0
{
429
0
  info->ref_count = 1;
430
431
0
  if (key != NULL)
432
0
    info->key = g_strdup (key);
433
434
0
  if (value != NULL)
435
0
    info->value = g_strdup (value);
436
437
0
  if (embedded_annotations != NULL)
438
0
    info->annotations = embedded_annotations;
439
0
}
440
441
static void
442
g_dbus_arg_info_set (ParseData            *data,
443
                     GDBusArgInfo         *info,
444
                     const gchar          *name,
445
                     const gchar          *signature,
446
                     GDBusAnnotationInfo **annotations)
447
0
{
448
0
  info->ref_count = 1;
449
450
  /* name may be NULL - TODO: compute name? */
451
0
  if (name != NULL)
452
0
    info->name = g_strdup (name);
453
454
0
  if (signature != NULL)
455
0
    info->signature = g_strdup (signature);
456
457
0
  if (annotations != NULL)
458
0
    info->annotations = annotations;
459
0
}
460
461
static void
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
0
{
469
0
  info->ref_count = 1;
470
471
0
  if (name != NULL)
472
0
    info->name = g_strdup (name);
473
474
0
  if (in_args != NULL)
475
0
    info->in_args = in_args;
476
477
0
  if (out_args != NULL)
478
0
    info->out_args = out_args;
479
480
0
  if (annotations != NULL)
481
0
    info->annotations = annotations;
482
0
}
483
484
static void
485
g_dbus_signal_info_set (ParseData            *data,
486
                        GDBusSignalInfo      *info,
487
                        const gchar          *name,
488
                        GDBusArgInfo        **args,
489
                        GDBusAnnotationInfo **annotations)
490
0
{
491
0
  info->ref_count = 1;
492
493
0
  if (name != NULL)
494
0
    info->name = g_strdup (name);
495
496
0
  if (args != NULL)
497
0
    info->args = args;
498
499
0
  if (annotations != NULL)
500
0
    info->annotations = annotations;
501
0
}
502
503
static void
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
0
{
511
0
  info->ref_count = 1;
512
513
0
  if (name != NULL)
514
0
    info->name = g_strdup (name);
515
516
0
  if (flags != G_DBUS_PROPERTY_INFO_FLAGS_NONE)
517
0
    info->flags = flags;
518
519
0
  if (signature != NULL)
520
0
    info->signature = g_strdup (signature);
521
522
0
  if (annotations != NULL)
523
0
    info->annotations = annotations;
524
0
}
525
526
static void
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
0
{
535
0
  info->ref_count = 1;
536
537
0
  if (name != NULL)
538
0
    info->name = g_strdup (name);
539
540
0
  if (methods != NULL)
541
0
    info->methods = methods;
542
543
0
  if (signals != NULL)
544
0
    info->signals = signals;
545
546
0
  if (properties != NULL)
547
0
    info->properties = properties;
548
549
0
  if (annotations != NULL)
550
0
    info->annotations = annotations;
551
0
}
552
553
static void
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
0
{
561
0
  info->ref_count = 1;
562
563
0
  if (path != NULL)
564
0
    {
565
0
      info->path = g_strdup (path);
566
      /* TODO: relative / absolute path snafu */
567
0
    }
568
569
0
  if (interfaces != NULL)
570
0
    info->interfaces = interfaces;
571
572
0
  if (nodes != NULL)
573
0
    info->nodes = nodes;
574
575
0
  if (annotations != NULL)
576
0
    info->annotations = annotations;
577
0
}
578
579
/* ---------------------------------------------------------------------------------------------------- */
580
581
static void
582
g_dbus_annotation_info_generate_xml (GDBusAnnotationInfo *info,
583
                                     guint                indent,
584
                                     GString             *string_builder)
585
0
{
586
0
  gchar *tmp;
587
0
  guint n;
588
589
0
  tmp = g_markup_printf_escaped ("%*s<annotation name=\"%s\" value=\"%s\"",
590
0
                                 indent, "",
591
0
                                 info->key,
592
0
                                 info->value);
593
0
  g_string_append (string_builder, tmp);
594
0
  g_free (tmp);
595
596
0
  if (info->annotations == NULL)
597
0
    {
598
0
      g_string_append (string_builder, "/>\n");
599
0
    }
600
0
  else
601
0
    {
602
0
      g_string_append (string_builder, ">\n");
603
604
0
      for (n = 0; info->annotations != NULL && info->annotations[n] != NULL; n++)
605
0
        g_dbus_annotation_info_generate_xml (info->annotations[n],
606
0
                                             indent + 2,
607
0
                                             string_builder);
608
609
0
      g_string_append_printf (string_builder, "%*s</annotation>\n",
610
0
                              indent, "");
611
0
    }
612
613
0
}
614
615
static void
616
g_dbus_arg_info_generate_xml (GDBusArgInfo *info,
617
                              guint         indent,
618
                              const gchar  *extra_attributes,
619
                              GString      *string_builder)
620
0
{
621
0
  guint n;
622
623
0
  g_string_append_printf (string_builder, "%*s<arg type=\"%s\"",
624
0
                          indent, "",
625
0
                          info->signature);
626
627
0
  if (info->name != NULL)
628
0
    g_string_append_printf (string_builder, " name=\"%s\"", info->name);
629
630
0
  if (extra_attributes != NULL)
631
0
    g_string_append_printf (string_builder, " %s", extra_attributes);
632
633
0
  if (info->annotations == NULL)
634
0
    {
635
0
      g_string_append (string_builder, "/>\n");
636
0
    }
637
0
  else
638
0
    {
639
0
      g_string_append (string_builder, ">\n");
640
641
0
      for (n = 0; info->annotations != NULL && info->annotations[n] != NULL; n++)
642
0
        g_dbus_annotation_info_generate_xml (info->annotations[n],
643
0
                                             indent + 2,
644
0
                                             string_builder);
645
646
0
      g_string_append_printf (string_builder, "%*s</arg>\n", indent, "");
647
0
    }
648
649
0
}
650
651
static void
652
g_dbus_method_info_generate_xml (GDBusMethodInfo *info,
653
                                 guint            indent,
654
                                 GString         *string_builder)
655
0
{
656
0
  guint n;
657
658
0
  g_string_append_printf (string_builder, "%*s<method name=\"%s\"",
659
0
                          indent, "",
660
0
                          info->name);
661
662
0
  if (info->annotations == NULL && info->in_args == NULL && info->out_args == NULL)
663
0
    {
664
0
      g_string_append (string_builder, "/>\n");
665
0
    }
666
0
  else
667
0
    {
668
0
      g_string_append (string_builder, ">\n");
669
670
0
      for (n = 0; info->annotations != NULL && info->annotations[n] != NULL; n++)
671
0
        g_dbus_annotation_info_generate_xml (info->annotations[n],
672
0
                                             indent + 2,
673
0
                                             string_builder);
674
675
0
      for (n = 0; info->in_args != NULL && info->in_args[n] != NULL; n++)
676
0
        g_dbus_arg_info_generate_xml (info->in_args[n],
677
0
                                      indent + 2,
678
0
                                      "direction=\"in\"",
679
0
                                      string_builder);
680
681
0
      for (n = 0; info->out_args != NULL && info->out_args[n] != NULL; n++)
682
0
        g_dbus_arg_info_generate_xml (info->out_args[n],
683
0
                                      indent + 2,
684
0
                                      "direction=\"out\"",
685
0
                                      string_builder);
686
687
0
      g_string_append_printf (string_builder, "%*s</method>\n", indent, "");
688
0
    }
689
0
}
690
691
static void
692
g_dbus_signal_info_generate_xml (GDBusSignalInfo *info,
693
                                 guint            indent,
694
                                 GString         *string_builder)
695
0
{
696
0
  guint n;
697
698
0
  g_string_append_printf (string_builder, "%*s<signal name=\"%s\"",
699
0
                          indent, "",
700
0
                          info->name);
701
702
0
  if (info->annotations == NULL && info->args == NULL)
703
0
    {
704
0
      g_string_append (string_builder, "/>\n");
705
0
    }
706
0
  else
707
0
    {
708
0
      g_string_append (string_builder, ">\n");
709
710
0
      for (n = 0; info->annotations != NULL && info->annotations[n] != NULL; n++)
711
0
        g_dbus_annotation_info_generate_xml (info->annotations[n],
712
0
                                             indent + 2,
713
0
                                             string_builder);
714
715
0
      for (n = 0; info->args != NULL && info->args[n] != NULL; n++)
716
0
        g_dbus_arg_info_generate_xml (info->args[n],
717
0
                                      indent + 2,
718
0
                                      NULL,
719
0
                                      string_builder);
720
721
0
      g_string_append_printf (string_builder, "%*s</signal>\n", indent, "");
722
0
    }
723
0
}
724
725
static void
726
g_dbus_property_info_generate_xml (GDBusPropertyInfo *info,
727
                                   guint              indent,
728
                                   GString           *string_builder)
729
0
{
730
0
  guint n;
731
0
  const gchar *access_string;
732
733
0
  if ((info->flags & G_DBUS_PROPERTY_INFO_FLAGS_READABLE) &&
734
0
      (info->flags & G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE))
735
0
    {
736
0
      access_string = "readwrite";
737
0
    }
738
0
  else if (info->flags & G_DBUS_PROPERTY_INFO_FLAGS_READABLE)
739
0
    {
740
0
      access_string = "read";
741
0
    }
742
0
  else if (info->flags & G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE)
743
0
    {
744
0
      access_string = "write";
745
0
    }
746
0
  else
747
0
    {
748
0
      g_assert_not_reached ();
749
0
    }
750
751
0
  g_string_append_printf (string_builder, "%*s<property type=\"%s\" name=\"%s\" access=\"%s\"",
752
0
                          indent, "",
753
0
                          info->signature,
754
0
                          info->name,
755
0
                          access_string);
756
757
0
  if (info->annotations == NULL)
758
0
    {
759
0
      g_string_append (string_builder, "/>\n");
760
0
    }
761
0
  else
762
0
    {
763
0
      g_string_append (string_builder, ">\n");
764
765
0
      for (n = 0; info->annotations != NULL && info->annotations[n] != NULL; n++)
766
0
        g_dbus_annotation_info_generate_xml (info->annotations[n],
767
0
                                               indent + 2,
768
0
                                               string_builder);
769
770
0
      g_string_append_printf (string_builder, "%*s</property>\n", indent, "");
771
0
    }
772
773
0
}
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
791
g_dbus_interface_info_generate_xml (GDBusInterfaceInfo *info,
792
                                    guint               indent,
793
                                    GString            *string_builder)
794
0
{
795
0
  guint n;
796
797
0
  g_string_append_printf (string_builder, "%*s<interface name=\"%s\">\n",
798
0
                          indent, "",
799
0
                          info->name);
800
801
0
  for (n = 0; info->annotations != NULL && info->annotations[n] != NULL; n++)
802
0
    g_dbus_annotation_info_generate_xml (info->annotations[n],
803
0
                                         indent + 2,
804
0
                                         string_builder);
805
806
0
  for (n = 0; info->methods != NULL && info->methods[n] != NULL; n++)
807
0
    g_dbus_method_info_generate_xml (info->methods[n],
808
0
                                     indent + 2,
809
0
                                     string_builder);
810
811
0
  for (n = 0; info->signals != NULL && info->signals[n] != NULL; n++)
812
0
    g_dbus_signal_info_generate_xml (info->signals[n],
813
0
                                     indent + 2,
814
0
                                     string_builder);
815
816
0
  for (n = 0; info->properties != NULL && info->properties[n] != NULL; n++)
817
0
    g_dbus_property_info_generate_xml (info->properties[n],
818
0
                                       indent + 2,
819
0
                                       string_builder);
820
821
0
  g_string_append_printf (string_builder, "%*s</interface>\n", indent, "");
822
0
}
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
838
g_dbus_node_info_generate_xml (GDBusNodeInfo *info,
839
                               guint          indent,
840
                               GString       *string_builder)
841
0
{
842
0
  guint n;
843
844
0
  g_string_append_printf (string_builder, "%*s<node", indent, "");
845
0
  if (info->path != NULL)
846
0
    g_string_append_printf (string_builder, " name=\"%s\"", info->path);
847
848
0
  if (info->interfaces == NULL && info->nodes == NULL && info->annotations == NULL)
849
0
    {
850
0
      g_string_append (string_builder, "/>\n");
851
0
    }
852
0
  else
853
0
    {
854
0
      g_string_append (string_builder, ">\n");
855
856
0
      for (n = 0; info->annotations != NULL && info->annotations[n] != NULL; n++)
857
0
        g_dbus_annotation_info_generate_xml (info->annotations[n],
858
0
                                             indent + 2,
859
0
                                             string_builder);
860
861
0
      for (n = 0; info->interfaces != NULL && info->interfaces[n] != NULL; n++)
862
0
        g_dbus_interface_info_generate_xml (info->interfaces[n],
863
0
                                            indent + 2,
864
0
                                            string_builder);
865
866
0
      for (n = 0; info->nodes != NULL && info->nodes[n] != NULL; n++)
867
0
        g_dbus_node_info_generate_xml (info->nodes[n],
868
0
                                       indent + 2,
869
0
                                       string_builder);
870
871
0
      g_string_append_printf (string_builder, "%*s</node>\n", indent, "");
872
0
    }
873
0
}
874
875
/* ---------------------------------------------------------------------------------------------------- */
876
877
static GDBusAnnotationInfo **
878
parse_data_steal_annotations (ParseData *data,
879
                              guint     *out_num_elements)
880
0
{
881
0
  GDBusAnnotationInfo **ret;
882
0
  if (out_num_elements != NULL)
883
0
    *out_num_elements = data->annotations->len;
884
0
  if (data->annotations == NULL)
885
0
    ret = NULL;
886
0
  else
887
0
    {
888
0
      g_ptr_array_add (data->annotations, NULL);
889
0
      ret = (GDBusAnnotationInfo **) g_ptr_array_free (data->annotations, FALSE);
890
0
    }
891
0
  data->annotations = g_ptr_array_new ();
892
0
  return ret;
893
0
}
894
895
static GDBusArgInfo **
896
parse_data_steal_args (ParseData *data,
897
                       guint     *out_num_elements)
898
0
{
899
0
  GDBusArgInfo **ret;
900
0
  if (out_num_elements != NULL)
901
0
    *out_num_elements = data->args->len;
902
0
  if (data->args == NULL)
903
0
    ret = NULL;
904
0
  else
905
0
    {
906
0
      g_ptr_array_add (data->args, NULL);
907
0
      ret = (GDBusArgInfo **) g_ptr_array_free (data->args, FALSE);
908
0
    }
909
0
  data->args = g_ptr_array_new ();
910
0
  return ret;
911
0
}
912
913
static GDBusArgInfo **
914
parse_data_steal_out_args (ParseData *data,
915
                           guint     *out_num_elements)
916
0
{
917
0
  GDBusArgInfo **ret;
918
0
  if (out_num_elements != NULL)
919
0
    *out_num_elements = data->out_args->len;
920
0
  if (data->out_args == NULL)
921
0
    ret = NULL;
922
0
  else
923
0
    {
924
0
      g_ptr_array_add (data->out_args, NULL);
925
0
      ret = (GDBusArgInfo **) g_ptr_array_free (data->out_args, FALSE);
926
0
    }
927
0
  data->out_args = g_ptr_array_new ();
928
0
  return ret;
929
0
}
930
931
static GDBusMethodInfo **
932
parse_data_steal_methods (ParseData *data,
933
                          guint     *out_num_elements)
934
0
{
935
0
  GDBusMethodInfo **ret;
936
0
  if (out_num_elements != NULL)
937
0
    *out_num_elements = data->methods->len;
938
0
  if (data->methods == NULL)
939
0
    ret = NULL;
940
0
  else
941
0
    {
942
0
      g_ptr_array_add (data->methods, NULL);
943
0
      ret = (GDBusMethodInfo **) g_ptr_array_free (data->methods, FALSE);
944
0
    }
945
0
  data->methods = g_ptr_array_new ();
946
0
  return ret;
947
0
}
948
949
static GDBusSignalInfo **
950
parse_data_steal_signals (ParseData *data,
951
                          guint     *out_num_elements)
952
0
{
953
0
  GDBusSignalInfo **ret;
954
0
  if (out_num_elements != NULL)
955
0
    *out_num_elements = data->signals->len;
956
0
  if (data->signals == NULL)
957
0
    ret = NULL;
958
0
  else
959
0
    {
960
0
      g_ptr_array_add (data->signals, NULL);
961
0
      ret = (GDBusSignalInfo **) g_ptr_array_free (data->signals, FALSE);
962
0
    }
963
0
  data->signals = g_ptr_array_new ();
964
0
  return ret;
965
0
}
966
967
static GDBusPropertyInfo **
968
parse_data_steal_properties (ParseData *data,
969
                             guint     *out_num_elements)
970
0
{
971
0
  GDBusPropertyInfo **ret;
972
0
  if (out_num_elements != NULL)
973
0
    *out_num_elements = data->properties->len;
974
0
  if (data->properties == NULL)
975
0
    ret = NULL;
976
0
  else
977
0
    {
978
0
      g_ptr_array_add (data->properties, NULL);
979
0
      ret = (GDBusPropertyInfo **) g_ptr_array_free (data->properties, FALSE);
980
0
    }
981
0
  data->properties = g_ptr_array_new ();
982
0
  return ret;
983
0
}
984
985
static GDBusInterfaceInfo **
986
parse_data_steal_interfaces (ParseData *data,
987
                             guint     *out_num_elements)
988
0
{
989
0
  GDBusInterfaceInfo **ret;
990
0
  if (out_num_elements != NULL)
991
0
    *out_num_elements = data->interfaces->len;
992
0
  if (data->interfaces == NULL)
993
0
    ret = NULL;
994
0
  else
995
0
    {
996
0
      g_ptr_array_add (data->interfaces, NULL);
997
0
      ret = (GDBusInterfaceInfo **) g_ptr_array_free (data->interfaces, FALSE);
998
0
    }
999
0
  data->interfaces = g_ptr_array_new ();
1000
0
  return ret;
1001
0
}
1002
1003
static GDBusNodeInfo **
1004
parse_data_steal_nodes (ParseData *data,
1005
                        guint     *out_num_elements)
1006
0
{
1007
0
  GDBusNodeInfo **ret;
1008
0
  if (out_num_elements != NULL)
1009
0
    *out_num_elements = data->nodes->len;
1010
0
  if (data->nodes == NULL)
1011
0
    ret = NULL;
1012
0
  else
1013
0
    {
1014
0
      g_ptr_array_add (data->nodes, NULL);
1015
0
      ret = (GDBusNodeInfo **) g_ptr_array_free (data->nodes, FALSE);
1016
0
    }
1017
0
  data->nodes = g_ptr_array_new ();
1018
0
  return ret;
1019
0
}
1020
1021
/* ---------------------------------------------------------------------------------------------------- */
1022
1023
static void
1024
parse_data_free_annotations (ParseData *data)
1025
0
{
1026
0
  if (data->annotations == NULL)
1027
0
    return;
1028
0
  g_ptr_array_foreach (data->annotations, (GFunc) g_dbus_annotation_info_unref, NULL);
1029
0
  g_ptr_array_free (data->annotations, TRUE);
1030
0
  data->annotations = NULL;
1031
0
}
1032
1033
static void
1034
parse_data_free_args (ParseData *data)
1035
0
{
1036
0
  if (data->args == NULL)
1037
0
    return;
1038
0
  g_ptr_array_foreach (data->args, (GFunc) g_dbus_arg_info_unref, NULL);
1039
0
  g_ptr_array_free (data->args, TRUE);
1040
0
  data->args = NULL;
1041
0
}
1042
1043
static void
1044
parse_data_free_out_args (ParseData *data)
1045
0
{
1046
0
  if (data->out_args == NULL)
1047
0
    return;
1048
0
  g_ptr_array_foreach (data->out_args, (GFunc) g_dbus_arg_info_unref, NULL);
1049
0
  g_ptr_array_free (data->out_args, TRUE);
1050
0
  data->out_args = NULL;
1051
0
}
1052
1053
static void
1054
parse_data_free_methods (ParseData *data)
1055
0
{
1056
0
  if (data->methods == NULL)
1057
0
    return;
1058
0
  g_ptr_array_foreach (data->methods, (GFunc) g_dbus_method_info_unref, NULL);
1059
0
  g_ptr_array_free (data->methods, TRUE);
1060
0
  data->methods = NULL;
1061
0
}
1062
1063
static void
1064
parse_data_free_signals (ParseData *data)
1065
0
{
1066
0
  if (data->signals == NULL)
1067
0
    return;
1068
0
  g_ptr_array_foreach (data->signals, (GFunc) g_dbus_signal_info_unref, NULL);
1069
0
  g_ptr_array_free (data->signals, TRUE);
1070
0
  data->signals = NULL;
1071
0
}
1072
1073
static void
1074
parse_data_free_properties (ParseData *data)
1075
0
{
1076
0
  if (data->properties == NULL)
1077
0
    return;
1078
0
  g_ptr_array_foreach (data->properties, (GFunc) g_dbus_property_info_unref, NULL);
1079
0
  g_ptr_array_free (data->properties, TRUE);
1080
0
  data->properties = NULL;
1081
0
}
1082
1083
static void
1084
parse_data_free_interfaces (ParseData *data)
1085
0
{
1086
0
  if (data->interfaces == NULL)
1087
0
    return;
1088
0
  g_ptr_array_foreach (data->interfaces, (GFunc) g_dbus_interface_info_unref, NULL);
1089
0
  g_ptr_array_free (data->interfaces, TRUE);
1090
0
  data->interfaces = NULL;
1091
0
}
1092
1093
static void
1094
parse_data_free_nodes (ParseData *data)
1095
0
{
1096
0
  if (data->nodes == NULL)
1097
0
    return;
1098
0
  g_ptr_array_foreach (data->nodes, (GFunc) g_dbus_node_info_unref, NULL);
1099
0
  g_ptr_array_free (data->nodes, TRUE);
1100
0
  data->nodes = NULL;
1101
0
}
1102
1103
/* ---------------------------------------------------------------------------------------------------- */
1104
1105
static GDBusAnnotationInfo *
1106
parse_data_get_annotation (ParseData *data,
1107
                           gboolean   create_new)
1108
0
{
1109
0
  if (create_new)
1110
0
    g_ptr_array_add (data->annotations, g_new0 (GDBusAnnotationInfo, 1));
1111
0
  return data->annotations->pdata[data->annotations->len - 1];
1112
0
}
1113
1114
static GDBusArgInfo *
1115
parse_data_get_arg (ParseData *data,
1116
                    gboolean   create_new)
1117
0
{
1118
0
  if (create_new)
1119
0
    g_ptr_array_add (data->args, g_new0 (GDBusArgInfo, 1));
1120
0
  return data->args->pdata[data->args->len - 1];
1121
0
}
1122
1123
static GDBusArgInfo *
1124
parse_data_get_out_arg (ParseData *data,
1125
                        gboolean   create_new)
1126
0
{
1127
0
  if (create_new)
1128
0
    g_ptr_array_add (data->out_args, g_new0 (GDBusArgInfo, 1));
1129
0
  return data->out_args->pdata[data->out_args->len - 1];
1130
0
}
1131
1132
static GDBusMethodInfo *
1133
parse_data_get_method (ParseData *data,
1134
                       gboolean   create_new)
1135
0
{
1136
0
  if (create_new)
1137
0
    g_ptr_array_add (data->methods, g_new0 (GDBusMethodInfo, 1));
1138
0
  return data->methods->pdata[data->methods->len - 1];
1139
0
}
1140
1141
static GDBusSignalInfo *
1142
parse_data_get_signal (ParseData *data,
1143
                       gboolean   create_new)
1144
0
{
1145
0
  if (create_new)
1146
0
    g_ptr_array_add (data->signals, g_new0 (GDBusSignalInfo, 1));
1147
0
  return data->signals->pdata[data->signals->len - 1];
1148
0
}
1149
1150
static GDBusPropertyInfo *
1151
parse_data_get_property (ParseData *data,
1152
                         gboolean   create_new)
1153
0
{
1154
0
  if (create_new)
1155
0
    g_ptr_array_add (data->properties, g_new0 (GDBusPropertyInfo, 1));
1156
0
  return data->properties->pdata[data->properties->len - 1];
1157
0
}
1158
1159
static GDBusInterfaceInfo *
1160
parse_data_get_interface (ParseData *data,
1161
                          gboolean   create_new)
1162
0
{
1163
0
  if (create_new)
1164
0
    g_ptr_array_add (data->interfaces, g_new0 (GDBusInterfaceInfo, 1));
1165
0
  return data->interfaces->pdata[data->interfaces->len - 1];
1166
0
}
1167
1168
static GDBusNodeInfo *
1169
parse_data_get_node (ParseData *data,
1170
                     gboolean   create_new)
1171
0
{
1172
0
  if (create_new)
1173
0
    g_ptr_array_add (data->nodes, g_new0 (GDBusNodeInfo, 1));
1174
0
  return data->nodes->pdata[data->nodes->len - 1];
1175
0
}
1176
1177
/* ---------------------------------------------------------------------------------------------------- */
1178
1179
static ParseData *
1180
parse_data_new (void)
1181
0
{
1182
0
  ParseData *data;
1183
1184
0
  data = g_new0 (ParseData, 1);
1185
1186
  /* initialize arrays */
1187
0
  parse_data_steal_annotations (data, NULL);
1188
0
  parse_data_steal_args (data, NULL);
1189
0
  parse_data_steal_out_args (data, NULL);
1190
0
  parse_data_steal_methods (data, NULL);
1191
0
  parse_data_steal_signals (data, NULL);
1192
0
  parse_data_steal_properties (data, NULL);
1193
0
  parse_data_steal_interfaces (data, NULL);
1194
0
  parse_data_steal_nodes (data, NULL);
1195
1196
0
  return data;
1197
0
}
1198
1199
static void
1200
parse_data_free (ParseData *data)
1201
0
{
1202
0
  GSList *l;
1203
1204
  /* free stack of annotation arrays */
1205
0
  for (l = data->annotations_stack; l != NULL; l = l->next)
1206
0
    {
1207
0
      GPtrArray *annotations = l->data;
1208
0
      g_ptr_array_foreach (annotations, (GFunc) g_dbus_annotation_info_unref, NULL);
1209
0
      g_ptr_array_free (annotations, TRUE);
1210
0
    }
1211
0
  g_slist_free (data->annotations_stack);
1212
1213
  /* free stack of interface arrays */
1214
0
  for (l = data->interfaces_stack; l != NULL; l = l->next)
1215
0
    {
1216
0
      GPtrArray *interfaces = l->data;
1217
0
      g_ptr_array_foreach (interfaces, (GFunc) g_dbus_interface_info_unref, NULL);
1218
0
      g_ptr_array_free (interfaces, TRUE);
1219
0
    }
1220
0
  g_slist_free (data->interfaces_stack);
1221
1222
  /* free stack of node arrays */
1223
0
  for (l = data->nodes_stack; l != NULL; l = l->next)
1224
0
    {
1225
0
      GPtrArray *nodes = l->data;
1226
0
      g_ptr_array_foreach (nodes, (GFunc) g_dbus_node_info_unref, NULL);
1227
0
      g_ptr_array_free (nodes, TRUE);
1228
0
    }
1229
0
  g_slist_free (data->nodes_stack);
1230
1231
  /* free arrays (data->annotations, data->interfaces and data->nodes have been freed above) */
1232
0
  parse_data_free_args (data);
1233
0
  parse_data_free_out_args (data);
1234
0
  parse_data_free_methods (data);
1235
0
  parse_data_free_signals (data);
1236
0
  parse_data_free_properties (data);
1237
0
  parse_data_free_interfaces (data);
1238
0
  parse_data_free_annotations (data);
1239
0
  parse_data_free_nodes (data);
1240
1241
0
  g_free (data);
1242
0
}
1243
1244
/* ---------------------------------------------------------------------------------------------------- */
1245
1246
static void
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
0
{
1254
0
  ParseData *data = user_data;
1255
0
  GSList *stack;
1256
0
  const gchar *name;
1257
0
  const gchar *type;
1258
0
  const gchar *access;
1259
0
  const gchar *direction;
1260
0
  const gchar *value;
1261
1262
0
  name = NULL;
1263
0
  type = NULL;
1264
0
  access = NULL;
1265
0
  direction = NULL;
1266
0
  value = NULL;
1267
1268
0
  stack = (GSList *) g_markup_parse_context_get_element_stack (context);
1269
1270
  /* ---------------------------------------------------------------------------------------------------- */
1271
0
  if (strcmp (element_name, "node") == 0)
1272
0
    {
1273
0
      if (!(g_slist_length (stack) >= 1 || strcmp (stack->next->data, "node") != 0))
1274
0
        {
1275
0
          g_set_error_literal (error,
1276
0
                               G_MARKUP_ERROR,
1277
0
                               G_MARKUP_ERROR_INVALID_CONTENT,
1278
0
                               "<node> elements can only be top-level or embedded in other <node> elements");
1279
0
          goto out;
1280
0
        }
1281
1282
0
      if (!g_markup_collect_attributes (element_name,
1283
0
                                        attribute_names,
1284
0
                                        attribute_values,
1285
0
                                        error,
1286
0
                                        G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL, "name", &name,
1287
                                        /* some hand-written introspection XML documents use this */
1288
0
                                        G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL, "xmlns:doc", NULL,
1289
0
                                        G_MARKUP_COLLECT_INVALID))
1290
0
        goto out;
1291
1292
0
      g_dbus_node_info_set (data,
1293
0
                            parse_data_get_node (data, TRUE),
1294
0
                            name,
1295
0
                            NULL,
1296
0
                            NULL,
1297
0
                            NULL);
1298
1299
      /* push the currently retrieved interfaces and nodes on the stack and prepare new arrays */
1300
0
      data->interfaces_stack = g_slist_prepend (data->interfaces_stack, data->interfaces);
1301
0
      data->interfaces = NULL;
1302
0
      parse_data_steal_interfaces (data, NULL);
1303
1304
0
      data->nodes_stack = g_slist_prepend (data->nodes_stack, data->nodes);
1305
0
      data->nodes = NULL;
1306
0
      parse_data_steal_nodes (data, NULL);
1307
1308
0
    }
1309
  /* ---------------------------------------------------------------------------------------------------- */
1310
0
  else if (strcmp (element_name, "interface") == 0)
1311
0
    {
1312
0
      if (g_slist_length (stack) < 2 || strcmp (stack->next->data, "node") != 0)
1313
0
        {
1314
0
          g_set_error_literal (error,
1315
0
                               G_MARKUP_ERROR,
1316
0
                               G_MARKUP_ERROR_INVALID_CONTENT,
1317
0
                               "<interface> elements can only be embedded in <node> elements");
1318
0
          goto out;
1319
0
        }
1320
1321
0
      if (!g_markup_collect_attributes (element_name,
1322
0
                                        attribute_names,
1323
0
                                        attribute_values,
1324
0
                                        error,
1325
0
                                        G_MARKUP_COLLECT_STRING, "name", &name,
1326
                                        /* seen in the wild */
1327
0
                                        G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL, "version", NULL,
1328
0
                                        G_MARKUP_COLLECT_INVALID))
1329
0
        goto out;
1330
1331
0
      g_dbus_interface_info_set (data,
1332
0
                                 parse_data_get_interface (data, TRUE),
1333
0
                                 name,
1334
0
                                 NULL,
1335
0
                                 NULL,
1336
0
                                 NULL,
1337
0
                                 NULL);
1338
1339
0
    }
1340
  /* ---------------------------------------------------------------------------------------------------- */
1341
0
  else if (strcmp (element_name, "method") == 0)
1342
0
    {
1343
0
      if (g_slist_length (stack) < 2 || strcmp (stack->next->data, "interface") != 0)
1344
0
        {
1345
0
          g_set_error_literal (error,
1346
0
                               G_MARKUP_ERROR,
1347
0
                               G_MARKUP_ERROR_INVALID_CONTENT,
1348
0
                               "<method> elements can only be embedded in <interface> elements");
1349
0
          goto out;
1350
0
        }
1351
1352
0
      if (!g_markup_collect_attributes (element_name,
1353
0
                                        attribute_names,
1354
0
                                        attribute_values,
1355
0
                                        error,
1356
0
                                        G_MARKUP_COLLECT_STRING, "name", &name,
1357
                                        /* seen in the wild */
1358
0
                                        G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL, "version", NULL,
1359
0
                                        G_MARKUP_COLLECT_INVALID))
1360
0
        goto out;
1361
1362
0
      g_dbus_method_info_set (data,
1363
0
                              parse_data_get_method (data, TRUE),
1364
0
                              name,
1365
0
                              NULL,
1366
0
                              NULL,
1367
0
                              NULL);
1368
1369
0
      data->num_args = 0;
1370
1371
0
    }
1372
  /* ---------------------------------------------------------------------------------------------------- */
1373
0
  else if (strcmp (element_name, "signal") == 0)
1374
0
    {
1375
0
      if (g_slist_length (stack) < 2 || strcmp (stack->next->data, "interface") != 0)
1376
0
        {
1377
0
          g_set_error_literal (error,
1378
0
                               G_MARKUP_ERROR,
1379
0
                               G_MARKUP_ERROR_INVALID_CONTENT,
1380
0
                               "<signal> elements can only be embedded in <interface> elements");
1381
0
          goto out;
1382
0
        }
1383
1384
0
      if (!g_markup_collect_attributes (element_name,
1385
0
                                        attribute_names,
1386
0
                                        attribute_values,
1387
0
                                        error,
1388
0
                                        G_MARKUP_COLLECT_STRING, "name", &name,
1389
0
                                        G_MARKUP_COLLECT_INVALID))
1390
0
        goto out;
1391
1392
0
      g_dbus_signal_info_set (data,
1393
0
                              parse_data_get_signal (data, TRUE),
1394
0
                              name,
1395
0
                              NULL,
1396
0
                              NULL);
1397
1398
0
      data->num_args = 0;
1399
1400
0
    }
1401
  /* ---------------------------------------------------------------------------------------------------- */
1402
0
  else if (strcmp (element_name, "property") == 0)
1403
0
    {
1404
0
      GDBusPropertyInfoFlags flags;
1405
1406
0
      if (g_slist_length (stack) < 2 || strcmp (stack->next->data, "interface") != 0)
1407
0
        {
1408
0
          g_set_error_literal (error,
1409
0
                               G_MARKUP_ERROR,
1410
0
                               G_MARKUP_ERROR_INVALID_CONTENT,
1411
0
                               "<property> elements can only be embedded in <interface> elements");
1412
0
          goto out;
1413
0
        }
1414
1415
0
      if (!g_markup_collect_attributes (element_name,
1416
0
                                        attribute_names,
1417
0
                                        attribute_values,
1418
0
                                        error,
1419
0
                                        G_MARKUP_COLLECT_STRING, "name", &name,
1420
0
                                        G_MARKUP_COLLECT_STRING, "type", &type,
1421
0
                                        G_MARKUP_COLLECT_STRING, "access", &access,
1422
0
                                        G_MARKUP_COLLECT_INVALID))
1423
0
        goto out;
1424
1425
0
      if (strcmp (access, "read") == 0)
1426
0
        flags = G_DBUS_PROPERTY_INFO_FLAGS_READABLE;
1427
0
      else if (strcmp (access, "write") == 0)
1428
0
        flags = G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE;
1429
0
      else if (strcmp (access, "readwrite") == 0)
1430
0
        flags = G_DBUS_PROPERTY_INFO_FLAGS_READABLE | G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE;
1431
0
      else
1432
0
        {
1433
0
          g_set_error (error,
1434
0
                       G_MARKUP_ERROR,
1435
0
                       G_MARKUP_ERROR_INVALID_CONTENT,
1436
0
                       "Unknown value '%s' of access attribute for element <property>",
1437
0
                       access);
1438
0
          goto out;
1439
0
        }
1440
1441
0
      g_dbus_property_info_set (data,
1442
0
                                parse_data_get_property (data, TRUE),
1443
0
                                name,
1444
0
                                type,
1445
0
                                flags,
1446
0
                                NULL);
1447
1448
0
    }
1449
  /* ---------------------------------------------------------------------------------------------------- */
1450
0
  else if (strcmp (element_name, "arg") == 0)
1451
0
    {
1452
0
      gboolean is_in;
1453
0
      gchar *name_to_use;
1454
1455
0
      if (g_slist_length (stack) < 2 ||
1456
0
          (strcmp (stack->next->data, "method") != 0 &&
1457
0
           strcmp (stack->next->data, "signal") != 0))
1458
0
        {
1459
0
          g_set_error_literal (error,
1460
0
                               G_MARKUP_ERROR,
1461
0
                               G_MARKUP_ERROR_INVALID_CONTENT,
1462
0
                               "<arg> elements can only be embedded in <method> or <signal> elements");
1463
0
          goto out;
1464
0
        }
1465
1466
0
      if (!g_markup_collect_attributes (element_name,
1467
0
                                        attribute_names,
1468
0
                                        attribute_values,
1469
0
                                        error,
1470
0
                                        G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL, "name", &name,
1471
0
                                        G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL, "direction", &direction,
1472
0
                                        G_MARKUP_COLLECT_STRING, "type", &type,
1473
0
                                        G_MARKUP_COLLECT_INVALID))
1474
0
        goto out;
1475
1476
0
      if (strcmp (stack->next->data, "method") == 0)
1477
0
        is_in = TRUE;
1478
0
      else
1479
0
        is_in = FALSE;
1480
0
      if (direction != NULL)
1481
0
        {
1482
0
          if (strcmp (direction, "in") == 0)
1483
0
            is_in = TRUE;
1484
0
          else if (strcmp (direction, "out") == 0)
1485
0
            is_in = FALSE;
1486
0
          else
1487
0
            {
1488
0
              g_set_error (error,
1489
0
                           G_MARKUP_ERROR,
1490
0
                           G_MARKUP_ERROR_INVALID_CONTENT,
1491
0
                           "Unknown value '%s' of direction attribute",
1492
0
                           direction);
1493
0
              goto out;
1494
0
            }
1495
0
        }
1496
1497
0
      if (is_in && strcmp (stack->next->data, "signal") == 0)
1498
0
        {
1499
0
          g_set_error_literal (error,
1500
0
                               G_MARKUP_ERROR,
1501
0
                               G_MARKUP_ERROR_INVALID_CONTENT,
1502
0
                               "Only direction 'out' is allowed for <arg> elements embedded in <signal>");
1503
0
          goto out;
1504
0
        }
1505
1506
0
      if (name == NULL)
1507
0
        name_to_use = g_strdup_printf ("arg_%d", data->num_args);
1508
0
      else
1509
0
        name_to_use = g_strdup (name);
1510
0
      data->num_args++;
1511
1512
0
      if (is_in)
1513
0
        {
1514
0
          g_dbus_arg_info_set (data,
1515
0
                               parse_data_get_arg (data, TRUE),
1516
0
                               name_to_use,
1517
0
                               type,
1518
0
                               NULL);
1519
0
          data->last_arg_was_in = TRUE;
1520
0
        }
1521
0
      else
1522
0
        {
1523
0
          g_dbus_arg_info_set (data,
1524
0
                               parse_data_get_out_arg (data, TRUE),
1525
0
                               name_to_use,
1526
0
                               type,
1527
0
                               NULL);
1528
0
          data->last_arg_was_in = FALSE;
1529
1530
0
        }
1531
1532
0
      g_free (name_to_use);
1533
0
    }
1534
  /* ---------------------------------------------------------------------------------------------------- */
1535
0
  else if (strcmp (element_name, "annotation") == 0)
1536
0
    {
1537
0
      if (g_slist_length (stack) < 2 ||
1538
0
          (strcmp (stack->next->data, "node") != 0 &&
1539
0
           strcmp (stack->next->data, "interface") != 0 &&
1540
0
           strcmp (stack->next->data, "signal") != 0 &&
1541
0
           strcmp (stack->next->data, "method") != 0 &&
1542
0
           strcmp (stack->next->data, "property") != 0 &&
1543
0
           strcmp (stack->next->data, "arg") != 0 &&
1544
0
           strcmp (stack->next->data, "annotation") != 0))
1545
0
        {
1546
0
          g_set_error_literal (error,
1547
0
                               G_MARKUP_ERROR,
1548
0
                               G_MARKUP_ERROR_INVALID_CONTENT,
1549
0
                               "<annotation> elements can only be embedded in <node>, <interface>, <signal>, <method>, <property>, <arg> or <annotation> elements");
1550
0
          goto out;
1551
0
        }
1552
1553
0
      if (!g_markup_collect_attributes (element_name,
1554
0
                                        attribute_names,
1555
0
                                        attribute_values,
1556
0
                                        error,
1557
0
                                        G_MARKUP_COLLECT_STRING, "name", &name,
1558
0
                                        G_MARKUP_COLLECT_STRING, "value", &value,
1559
0
                                        G_MARKUP_COLLECT_INVALID))
1560
0
        goto out;
1561
1562
0
      g_dbus_annotation_info_set (data,
1563
0
                                  parse_data_get_annotation (data, TRUE),
1564
0
                                  name,
1565
0
                                  value,
1566
0
                                  NULL);
1567
0
    }
1568
  /* ---------------------------------------------------------------------------------------------------- */
1569
0
  else
1570
0
    {
1571
      /* don't bail on unknown elements; just ignore them */
1572
0
    }
1573
  /* ---------------------------------------------------------------------------------------------------- */
1574
1575
  /* push the currently retrieved annotations on the stack and prepare a new one */
1576
0
  data->annotations_stack = g_slist_prepend (data->annotations_stack, data->annotations);
1577
0
  data->annotations = NULL;
1578
0
  parse_data_steal_annotations (data, NULL);
1579
1580
0
 out:
1581
0
  ;
1582
0
}
1583
1584
/* ---------------------------------------------------------------------------------------------------- */
1585
1586
static GDBusAnnotationInfo **
1587
steal_annotations (ParseData *data)
1588
0
{
1589
0
  return parse_data_steal_annotations (data, NULL);
1590
0
}
1591
1592
1593
static void
1594
parser_end_element (GMarkupParseContext  *context,
1595
                    const gchar          *element_name,
1596
                    gpointer              user_data,
1597
                    GError              **error)
1598
0
{
1599
0
  ParseData *data = user_data;
1600
0
  gboolean have_popped_annotations;
1601
1602
0
  have_popped_annotations = FALSE;
1603
1604
0
  if (strcmp (element_name, "node") == 0)
1605
0
    {
1606
0
      guint num_nodes;
1607
0
      guint num_interfaces;
1608
0
      GDBusNodeInfo **nodes;
1609
0
      GDBusInterfaceInfo **interfaces;
1610
1611
0
      nodes = parse_data_steal_nodes (data, &num_nodes);
1612
0
      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
0
      parse_data_free_interfaces (data);
1618
0
      data->interfaces = (GPtrArray *) data->interfaces_stack->data;
1619
0
      data->interfaces_stack = g_slist_remove (data->interfaces_stack, data->interfaces_stack->data);
1620
1621
0
      parse_data_free_nodes (data);
1622
0
      data->nodes = (GPtrArray *) data->nodes_stack->data;
1623
0
      data->nodes_stack = g_slist_remove (data->nodes_stack, data->nodes_stack->data);
1624
1625
0
      g_dbus_node_info_set (data,
1626
0
                            parse_data_get_node (data, FALSE),
1627
0
                            NULL,
1628
0
                            interfaces,
1629
0
                            nodes,
1630
0
                            steal_annotations (data));
1631
1632
0
    }
1633
0
  else if (strcmp (element_name, "interface") == 0)
1634
0
    {
1635
0
      guint num_methods;
1636
0
      guint num_signals;
1637
0
      guint num_properties;
1638
0
      GDBusMethodInfo **methods;
1639
0
      GDBusSignalInfo **signals;
1640
0
      GDBusPropertyInfo **properties;
1641
1642
0
      methods    = parse_data_steal_methods    (data, &num_methods);
1643
0
      signals    = parse_data_steal_signals    (data, &num_signals);
1644
0
      properties = parse_data_steal_properties (data, &num_properties);
1645
1646
0
      g_dbus_interface_info_set (data,
1647
0
                                 parse_data_get_interface (data, FALSE),
1648
0
                                 NULL,
1649
0
                                 methods,
1650
0
                                 signals,
1651
0
                                 properties,
1652
0
                                 steal_annotations (data));
1653
1654
0
    }
1655
0
  else if (strcmp (element_name, "method") == 0)
1656
0
    {
1657
0
      guint in_num_args;
1658
0
      guint out_num_args;
1659
0
      GDBusArgInfo **in_args;
1660
0
      GDBusArgInfo **out_args;
1661
1662
0
      in_args  = parse_data_steal_args     (data, &in_num_args);
1663
0
      out_args = parse_data_steal_out_args (data, &out_num_args);
1664
1665
0
      g_dbus_method_info_set (data,
1666
0
                              parse_data_get_method (data, FALSE),
1667
0
                              NULL,
1668
0
                              in_args,
1669
0
                              out_args,
1670
0
                              steal_annotations (data));
1671
0
    }
1672
0
  else if (strcmp (element_name, "signal") == 0)
1673
0
    {
1674
0
      guint num_args;
1675
0
      GDBusArgInfo **args;
1676
1677
0
      args = parse_data_steal_out_args (data, &num_args);
1678
1679
0
      g_dbus_signal_info_set (data,
1680
0
                              parse_data_get_signal (data, FALSE),
1681
0
                              NULL,
1682
0
                              args,
1683
0
                              steal_annotations (data));
1684
0
    }
1685
0
  else if (strcmp (element_name, "property") == 0)
1686
0
    {
1687
0
      g_dbus_property_info_set (data,
1688
0
                                parse_data_get_property (data, FALSE),
1689
0
                                NULL,
1690
0
                                NULL,
1691
0
                                G_DBUS_PROPERTY_INFO_FLAGS_NONE,
1692
0
                                steal_annotations (data));
1693
0
    }
1694
0
  else if (strcmp (element_name, "arg") == 0)
1695
0
    {
1696
0
      g_dbus_arg_info_set (data,
1697
0
                           data->last_arg_was_in ? parse_data_get_arg (data, FALSE) : parse_data_get_out_arg (data, FALSE),
1698
0
                           NULL,
1699
0
                           NULL,
1700
0
                           steal_annotations (data));
1701
0
    }
1702
0
  else if (strcmp (element_name, "annotation") == 0)
1703
0
    {
1704
0
      GDBusAnnotationInfo **embedded_annotations;
1705
1706
0
      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
0
      parse_data_free_annotations (data);
1710
0
      data->annotations = (GPtrArray *) data->annotations_stack->data;
1711
0
      data->annotations_stack = g_slist_remove (data->annotations_stack, data->annotations_stack->data);
1712
1713
0
      have_popped_annotations = TRUE;
1714
1715
0
      g_dbus_annotation_info_set (data,
1716
0
                                  parse_data_get_annotation (data, FALSE),
1717
0
                                  NULL,
1718
0
                                  NULL,
1719
0
                                  embedded_annotations);
1720
0
    }
1721
0
  else
1722
0
    {
1723
      /* don't bail on unknown elements; just ignore them */
1724
0
    }
1725
1726
0
  if (!have_popped_annotations)
1727
0
    {
1728
      /* destroy the annotations for scope we're exiting and and pop the annotations from the scope we're reentering */
1729
0
      parse_data_free_annotations (data);
1730
0
      data->annotations = (GPtrArray *) data->annotations_stack->data;
1731
0
      data->annotations_stack = g_slist_remove (data->annotations_stack, data->annotations_stack->data);
1732
0
    }
1733
0
}
1734
1735
/* ---------------------------------------------------------------------------------------------------- */
1736
1737
static void
1738
parser_error (GMarkupParseContext *context,
1739
              GError              *error,
1740
              gpointer             user_data)
1741
0
{
1742
0
  gint line_number;
1743
0
  gint char_number;
1744
1745
0
  g_markup_parse_context_get_position (context, &line_number, &char_number);
1746
1747
0
  g_prefix_error (&error, "%d:%d: ",
1748
0
                  line_number,
1749
0
                  char_number);
1750
0
}
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 *
1774
g_dbus_node_info_new_for_xml (const gchar  *xml_data,
1775
                              GError      **error)
1776
0
{
1777
0
  GDBusNodeInfo *ret;
1778
0
  GMarkupParseContext *context;
1779
0
  GMarkupParser *parser;
1780
0
  guint num_nodes;
1781
0
  ParseData *data;
1782
0
  GDBusNodeInfo **ughret;
1783
1784
0
  ret = NULL;
1785
0
  parser = NULL;
1786
0
  context = NULL;
1787
1788
0
  parser = g_new0 (GMarkupParser, 1);
1789
0
  parser->start_element = parser_start_element;
1790
0
  parser->end_element   = parser_end_element;
1791
0
  parser->error         = parser_error;
1792
1793
0
  data = parse_data_new ();
1794
0
  context = g_markup_parse_context_new (parser,
1795
0
                                        G_MARKUP_IGNORE_QUALIFIED,
1796
0
                                        data,
1797
0
                                        (GDestroyNotify) parse_data_free);
1798
1799
0
  if (!g_markup_parse_context_parse (context,
1800
0
                                     xml_data,
1801
0
                                     strlen (xml_data),
1802
0
                                     error))
1803
0
    goto out;
1804
1805
0
  if (!g_markup_parse_context_end_parse (context, error))
1806
0
    goto out;
1807
1808
0
  ughret = parse_data_steal_nodes (data, &num_nodes);
1809
1810
0
  if (num_nodes != 1)
1811
0
    {
1812
0
      guint n;
1813
1814
0
      g_set_error (error,
1815
0
                   G_MARKUP_ERROR,
1816
0
                   G_MARKUP_ERROR_INVALID_CONTENT,
1817
0
                   "Expected a single node in introspection XML, found %d",
1818
0
                   num_nodes);
1819
1820
      /* clean up */
1821
0
      for (n = 0; n < num_nodes; n++)
1822
0
        {
1823
0
          g_dbus_node_info_unref (ughret[n]);
1824
0
          ughret[n] = NULL;
1825
0
        }
1826
0
    }
1827
1828
0
  ret = ughret[0];
1829
0
  g_free (ughret);
1830
1831
0
 out:
1832
0
  g_free (parser);
1833
0
  if (context != NULL)
1834
0
    g_markup_parse_context_free (context);
1835
1836
0
  return ret;
1837
0
}
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 *
1855
g_dbus_annotation_info_lookup (GDBusAnnotationInfo **annotations,
1856
                               const gchar          *name)
1857
0
{
1858
0
  guint n;
1859
0
  const gchar *ret;
1860
1861
0
  ret = NULL;
1862
0
  for (n = 0; annotations != NULL && annotations[n] != NULL; n++)
1863
0
    {
1864
0
      if (g_strcmp0 (annotations[n]->key, name) == 0)
1865
0
        {
1866
0
          ret = annotations[n]->value;
1867
0
          goto out;
1868
0
        }
1869
0
    }
1870
1871
0
 out:
1872
0
  return ret;
1873
0
}
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
1894
info_cache_free (InfoCacheEntry *cache)
1895
0
{
1896
0
  g_assert (cache->use_count == 0);
1897
0
  g_hash_table_unref (cache->method_name_to_data);
1898
0
  g_hash_table_unref (cache->signal_name_to_data);
1899
0
  g_hash_table_unref (cache->property_name_to_data);
1900
0
  g_slice_free (InfoCacheEntry, cache);
1901
0
}
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 *
1923
g_dbus_interface_info_lookup_method (GDBusInterfaceInfo *info,
1924
                                     const gchar        *name)
1925
0
{
1926
0
  guint n;
1927
0
  GDBusMethodInfo *result;
1928
1929
0
  G_LOCK (info_cache_lock);
1930
0
  if (G_LIKELY (info_cache != NULL))
1931
0
    {
1932
0
      InfoCacheEntry *cache;
1933
0
      cache = g_hash_table_lookup (info_cache, info);
1934
0
      if (G_LIKELY (cache != NULL))
1935
0
        {
1936
0
          result = g_hash_table_lookup (cache->method_name_to_data, name);
1937
0
          G_UNLOCK (info_cache_lock);
1938
0
          goto out;
1939
0
        }
1940
0
    }
1941
0
  G_UNLOCK (info_cache_lock);
1942
1943
0
  for (n = 0; info->methods != NULL && info->methods[n] != NULL; n++)
1944
0
    {
1945
0
      GDBusMethodInfo *i = info->methods[n];
1946
1947
0
      if (g_strcmp0 (i->name, name) == 0)
1948
0
        {
1949
0
          result = i;
1950
0
          goto out;
1951
0
        }
1952
0
    }
1953
1954
0
  result = NULL;
1955
1956
0
 out:
1957
0
  return result;
1958
0
}
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 *
1977
g_dbus_interface_info_lookup_signal (GDBusInterfaceInfo *info,
1978
                                     const gchar        *name)
1979
0
{
1980
0
  guint n;
1981
0
  GDBusSignalInfo *result;
1982
1983
0
  G_LOCK (info_cache_lock);
1984
0
  if (G_LIKELY (info_cache != NULL))
1985
0
    {
1986
0
      InfoCacheEntry *cache;
1987
0
      cache = g_hash_table_lookup (info_cache, info);
1988
0
      if (G_LIKELY (cache != NULL))
1989
0
        {
1990
0
          result = g_hash_table_lookup (cache->signal_name_to_data, name);
1991
0
          G_UNLOCK (info_cache_lock);
1992
0
          goto out;
1993
0
        }
1994
0
    }
1995
0
  G_UNLOCK (info_cache_lock);
1996
1997
0
  for (n = 0; info->signals != NULL && info->signals[n] != NULL; n++)
1998
0
    {
1999
0
      GDBusSignalInfo *i = info->signals[n];
2000
2001
0
      if (g_strcmp0 (i->name, name) == 0)
2002
0
        {
2003
0
          result = i;
2004
0
          goto out;
2005
0
        }
2006
0
    }
2007
2008
0
  result = NULL;
2009
2010
0
 out:
2011
0
  return result;
2012
0
}
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 *
2031
g_dbus_interface_info_lookup_property (GDBusInterfaceInfo *info,
2032
                                       const gchar        *name)
2033
0
{
2034
0
  guint n;
2035
0
  GDBusPropertyInfo *result;
2036
2037
0
  G_LOCK (info_cache_lock);
2038
0
  if (G_LIKELY (info_cache != NULL))
2039
0
    {
2040
0
      InfoCacheEntry *cache;
2041
0
      cache = g_hash_table_lookup (info_cache, info);
2042
0
      if (G_LIKELY (cache != NULL))
2043
0
        {
2044
0
          result = g_hash_table_lookup (cache->property_name_to_data, name);
2045
0
          G_UNLOCK (info_cache_lock);
2046
0
          goto out;
2047
0
        }
2048
0
    }
2049
0
  G_UNLOCK (info_cache_lock);
2050
2051
0
  for (n = 0; info->properties != NULL && info->properties[n] != NULL; n++)
2052
0
    {
2053
0
      GDBusPropertyInfo *i = info->properties[n];
2054
2055
0
      if (g_strcmp0 (i->name, name) == 0)
2056
0
        {
2057
0
          result = i;
2058
0
          goto out;
2059
0
        }
2060
0
    }
2061
2062
0
  result = NULL;
2063
2064
0
 out:
2065
0
  return result;
2066
0
}
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
2088
g_dbus_interface_info_cache_build (GDBusInterfaceInfo *info)
2089
0
{
2090
0
  InfoCacheEntry *cache;
2091
0
  guint n;
2092
2093
0
  G_LOCK (info_cache_lock);
2094
0
  if (info_cache == NULL)
2095
0
    info_cache = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) info_cache_free);
2096
0
  cache = g_hash_table_lookup (info_cache, info);
2097
0
  if (cache != NULL)
2098
0
    {
2099
0
      cache->use_count += 1;
2100
0
      goto out;
2101
0
    }
2102
0
  cache = g_slice_new0 (InfoCacheEntry);
2103
0
  cache->use_count = 1;
2104
0
  cache->method_name_to_data = g_hash_table_new (g_str_hash, g_str_equal);
2105
0
  cache->signal_name_to_data = g_hash_table_new (g_str_hash, g_str_equal);
2106
0
  cache->property_name_to_data = g_hash_table_new (g_str_hash, g_str_equal);
2107
0
  for (n = 0; info->methods != NULL && info->methods[n] != NULL; n++)
2108
0
    g_hash_table_insert (cache->method_name_to_data, info->methods[n]->name, info->methods[n]);
2109
0
  for (n = 0; info->signals != NULL && info->signals[n] != NULL; n++)
2110
0
    g_hash_table_insert (cache->signal_name_to_data, info->signals[n]->name, info->signals[n]);
2111
0
  for (n = 0; info->properties != NULL && info->properties[n] != NULL; n++)
2112
0
    g_hash_table_insert (cache->property_name_to_data, info->properties[n]->name, info->properties[n]);
2113
0
  g_hash_table_insert (info_cache, info, cache);
2114
0
 out:
2115
0
  G_UNLOCK (info_cache_lock);
2116
0
}
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
2129
g_dbus_interface_info_cache_release (GDBusInterfaceInfo *info)
2130
0
{
2131
0
  InfoCacheEntry *cache;
2132
2133
0
  G_LOCK (info_cache_lock);
2134
0
  if (G_UNLIKELY (info_cache == NULL))
2135
0
    {
2136
0
      g_warning ("%s called for interface %s but there is no cache", info->name, G_STRFUNC);
2137
0
      goto out;
2138
0
    }
2139
2140
0
  cache = g_hash_table_lookup (info_cache, info);
2141
0
  if (G_UNLIKELY (cache == NULL))
2142
0
    {
2143
0
      g_warning ("%s called for interface %s but there is no cache entry", info->name, G_STRFUNC);
2144
0
      goto out;
2145
0
    }
2146
0
  cache->use_count -= 1;
2147
0
  if (cache->use_count == 0)
2148
0
    {
2149
0
      g_hash_table_remove (info_cache, info);
2150
      /* could nuke info_cache itself if empty */
2151
0
    }
2152
0
 out:
2153
0
  G_UNLOCK (info_cache_lock);
2154
0
}
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 *
2173
g_dbus_node_info_lookup_interface (GDBusNodeInfo *info,
2174
                                   const gchar   *name)
2175
0
{
2176
0
  guint n;
2177
0
  GDBusInterfaceInfo *result;
2178
2179
0
  for (n = 0; info->interfaces != NULL && info->interfaces[n] != NULL; n++)
2180
0
    {
2181
0
      GDBusInterfaceInfo *i = info->interfaces[n];
2182
2183
0
      if (g_strcmp0 (i->name, name) == 0)
2184
0
        {
2185
0
          result = i;
2186
0
          goto out;
2187
0
        }
2188
0
    }
2189
2190
0
  result = NULL;
2191
2192
0
 out:
2193
0
  return result;
2194
0
}