1 /*
2  * This file is part of libmodulemd
3  * Copyright (C) 2017-2020 Stephen Gallagher
4  *
5  * Fedora-License-Identifier: MIT
6  * SPDX-2.0-License-Identifier: MIT
7  * SPDX-3.0-License-Identifier: MIT
8  *
9  * This program is free software.
10  * For more information on the license, see COPYING.
11  * For more information on free software, see <https://www.gnu.org/philosophy/free-sw.en.html>.
12  */
13 
14 #include "modulemd-errors.h"
15 #include "private/modulemd-subdocument-info-private.h"
16 #include "private/modulemd-util.h"
17 #include "private/modulemd-yaml.h"
18 #include <errno.h>
19 #include <glib.h>
20 #include <inttypes.h>
21 #include <yaml.h>
22 
23 
24 GQuark
modulemd_yaml_error_quark(void)25 modulemd_yaml_error_quark (void)
26 {
27   return g_quark_from_static_string ("modulemd-yaml-error-quark");
28 }
29 
30 
31 void
modulemd_yaml_string_free(modulemd_yaml_string * yaml_string)32 modulemd_yaml_string_free (modulemd_yaml_string *yaml_string)
33 {
34   g_clear_pointer (&yaml_string->str, g_free);
35   g_clear_pointer (&yaml_string, g_free);
36 }
37 
38 
39 int
write_yaml_string(void * data,unsigned char * buffer,size_t size)40 write_yaml_string (void *data, unsigned char *buffer, size_t size)
41 {
42   modulemd_yaml_string *yaml_string = (modulemd_yaml_string *)data;
43   gsize total;
44 
45   if (!g_size_checked_add (&total, yaml_string->len, size + 1))
46     {
47       return 0;
48     }
49 
50   yaml_string->str = g_realloc_n (yaml_string->str, total, sizeof (char));
51 
52   memcpy (yaml_string->str + yaml_string->len, buffer, size);
53   yaml_string->len += size;
54   yaml_string->str[yaml_string->len] = '\0';
55 
56   return 1;
57 }
58 
59 
60 const gchar *
mmd_yaml_get_event_name(yaml_event_type_t type)61 mmd_yaml_get_event_name (yaml_event_type_t type)
62 {
63   switch (type)
64     {
65     case YAML_NO_EVENT: return "YAML_NO_EVENT";
66 
67     case YAML_STREAM_START_EVENT: return "YAML_STREAM_START_EVENT";
68 
69     case YAML_STREAM_END_EVENT: return "YAML_STREAM_END_EVENT";
70 
71     case YAML_DOCUMENT_START_EVENT: return "YAML_DOCUMENT_START_EVENT";
72 
73     case YAML_DOCUMENT_END_EVENT: return "YAML_DOCUMENT_END_EVENT";
74 
75     case YAML_ALIAS_EVENT: return "YAML_ALIAS_EVENT";
76 
77     case YAML_SCALAR_EVENT: return "YAML_SCALAR_EVENT";
78 
79     case YAML_SEQUENCE_START_EVENT: return "YAML_SEQUENCE_START_EVENT";
80 
81     case YAML_SEQUENCE_END_EVENT: return "YAML_SEQUENCE_END_EVENT";
82 
83     case YAML_MAPPING_START_EVENT: return "YAML_MAPPING_START_EVENT";
84 
85     case YAML_MAPPING_END_EVENT: return "YAML_MAPPING_END_EVENT";
86     }
87 
88   /* Should be unreachable */
89   return "Unknown YAML Event";
90 }
91 
92 
93 gboolean
mmd_emitter_start_stream(yaml_emitter_t * emitter,GError ** error)94 mmd_emitter_start_stream (yaml_emitter_t *emitter, GError **error)
95 {
96   int ret;
97   MMD_INIT_YAML_EVENT (event);
98 
99   yaml_emitter_set_unicode (emitter, TRUE);
100 
101   ret = yaml_stream_start_event_initialize (&event, YAML_UTF8_ENCODING);
102   if (!ret)
103     {
104       g_set_error (error,
105                    MODULEMD_YAML_ERROR,
106                    MMD_YAML_ERROR_EVENT_INIT,
107                    "Could not initialize the stream start event");
108       return FALSE;
109     }
110 
111   MMD_EMIT_WITH_EXIT (
112     emitter, &event, error, "Could not start the YAML stream");
113 
114   return TRUE;
115 }
116 
117 
118 gboolean
mmd_emitter_end_stream(yaml_emitter_t * emitter,GError ** error)119 mmd_emitter_end_stream (yaml_emitter_t *emitter, GError **error)
120 {
121   int ret;
122   MMD_INIT_YAML_EVENT (event);
123 
124   ret = yaml_stream_end_event_initialize (&event);
125   if (!ret)
126     {
127       g_set_error (error,
128                    MODULEMD_YAML_ERROR,
129                    MMD_YAML_ERROR_EVENT_INIT,
130                    "Could not initialize the stream end event");
131       return FALSE;
132     }
133 
134   MMD_EMIT_WITH_EXIT (emitter, &event, error, "Could not end the YAML stream");
135 
136   return TRUE;
137 }
138 
139 
140 gboolean
mmd_emitter_start_document(yaml_emitter_t * emitter,GError ** error)141 mmd_emitter_start_document (yaml_emitter_t *emitter, GError **error)
142 {
143   int ret;
144   MMD_INIT_YAML_EVENT (event);
145 
146   ret = yaml_document_start_event_initialize (&event, NULL, NULL, NULL, 0);
147   if (!ret)
148     {
149       g_set_error (error,
150                    MODULEMD_YAML_ERROR,
151                    MMD_YAML_ERROR_EVENT_INIT,
152                    "Could not initialize the document start event");
153       return FALSE;
154     }
155 
156   MMD_EMIT_WITH_EXIT (
157     emitter, &event, error, "Could not start the YAML document");
158 
159   return TRUE;
160 }
161 
162 
163 gboolean
mmd_emitter_end_document(yaml_emitter_t * emitter,GError ** error)164 mmd_emitter_end_document (yaml_emitter_t *emitter, GError **error)
165 {
166   int ret;
167   MMD_INIT_YAML_EVENT (event);
168 
169   ret = yaml_document_end_event_initialize (&event, 0);
170   if (!ret)
171     {
172       g_set_error (error,
173                    MODULEMD_YAML_ERROR,
174                    MMD_YAML_ERROR_EVENT_INIT,
175                    "Could not initialize the document end event");
176       return FALSE;
177     }
178 
179   MMD_EMIT_WITH_EXIT (
180     emitter, &event, error, "Could not end the YAML document");
181 
182   return TRUE;
183 }
184 
185 
186 gboolean
mmd_emitter_start_mapping(yaml_emitter_t * emitter,yaml_mapping_style_t style,GError ** error)187 mmd_emitter_start_mapping (yaml_emitter_t *emitter,
188                            yaml_mapping_style_t style,
189                            GError **error)
190 {
191   int ret;
192   MMD_INIT_YAML_EVENT (event);
193 
194   ret = yaml_mapping_start_event_initialize (&event, NULL, NULL, 1, style);
195   if (!ret)
196     {
197       g_set_error (error,
198                    MODULEMD_YAML_ERROR,
199                    MMD_YAML_ERROR_EVENT_INIT,
200                    "Could not initialize the mapping start event");
201       return FALSE;
202     }
203 
204   MMD_EMIT_WITH_EXIT (emitter, &event, error, "Could not start the mapping");
205 
206   return TRUE;
207 }
208 
209 
210 gboolean
mmd_emitter_end_mapping(yaml_emitter_t * emitter,GError ** error)211 mmd_emitter_end_mapping (yaml_emitter_t *emitter, GError **error)
212 {
213   int ret;
214   MMD_INIT_YAML_EVENT (event);
215 
216   ret = yaml_mapping_end_event_initialize (&event);
217   if (!ret)
218     {
219       g_set_error (error,
220                    MODULEMD_YAML_ERROR,
221                    MMD_YAML_ERROR_EVENT_INIT,
222                    "Could not initialize the mapping end event");
223       return FALSE;
224     }
225 
226   MMD_EMIT_WITH_EXIT (emitter, &event, error, "Could not end the mapping");
227 
228   return TRUE;
229 }
230 
231 
232 gboolean
mmd_emitter_start_sequence(yaml_emitter_t * emitter,yaml_sequence_style_t style,GError ** error)233 mmd_emitter_start_sequence (yaml_emitter_t *emitter,
234                             yaml_sequence_style_t style,
235                             GError **error)
236 {
237   int ret;
238   MMD_INIT_YAML_EVENT (event);
239 
240   ret = yaml_sequence_start_event_initialize (&event, NULL, NULL, 1, style);
241   if (!ret)
242     {
243       g_set_error (error,
244                    MODULEMD_YAML_ERROR,
245                    MMD_YAML_ERROR_EVENT_INIT,
246                    "Could not initialize the sequence start event");
247       return FALSE;
248     }
249 
250   MMD_EMIT_WITH_EXIT (emitter, &event, error, "Could not start the sequence");
251 
252   return TRUE;
253 }
254 
255 
256 gboolean
mmd_emitter_end_sequence(yaml_emitter_t * emitter,GError ** error)257 mmd_emitter_end_sequence (yaml_emitter_t *emitter, GError **error)
258 {
259   int ret;
260   MMD_INIT_YAML_EVENT (event);
261 
262   ret = yaml_sequence_end_event_initialize (&event);
263   if (!ret)
264     {
265       g_set_error (error,
266                    MODULEMD_YAML_ERROR,
267                    MMD_YAML_ERROR_EVENT_INIT,
268                    "Could not initialize the sequence end event");
269       return FALSE;
270     }
271 
272   MMD_EMIT_WITH_EXIT (emitter, &event, error, "Could not end the sequence");
273 
274   return TRUE;
275 }
276 
277 
278 gboolean
mmd_emitter_scalar(yaml_emitter_t * emitter,const gchar * scalar,yaml_scalar_style_t style,GError ** error)279 mmd_emitter_scalar (yaml_emitter_t *emitter,
280                     const gchar *scalar,
281                     yaml_scalar_style_t style,
282                     GError **error)
283 {
284   int ret;
285   MMD_INIT_YAML_EVENT (event);
286 
287   g_debug ("SCALAR: %s", scalar);
288   ret = yaml_scalar_event_initialize (&event,
289                                       NULL,
290                                       NULL,
291                                       (yaml_char_t *)scalar,
292                                       (int)strlen (scalar),
293                                       1,
294                                       1,
295                                       style);
296   if (!ret)
297     {
298       g_set_error (error,
299                    MODULEMD_YAML_ERROR,
300                    MMD_YAML_ERROR_EVENT_INIT,
301                    "Could not initialize the scalar event");
302       return FALSE;
303     }
304 
305   MMD_EMIT_WITH_EXIT (emitter, &event, error, "Could not emit scalar value");
306 
307   return TRUE;
308 }
309 
310 
311 gboolean
mmd_emitter_strv(yaml_emitter_t * emitter,yaml_sequence_style_t seq_style,const GStrv list,GError ** error)312 mmd_emitter_strv (yaml_emitter_t *emitter,
313                   yaml_sequence_style_t seq_style,
314                   const GStrv list,
315                   GError **error)
316 {
317   int ret;
318   g_autoptr (GError) nested_error = NULL;
319   MMD_INIT_YAML_EVENT (event);
320   int numentries = g_strv_length (list);
321 
322   ret = mmd_emitter_start_sequence (emitter, seq_style, &nested_error);
323   if (!ret)
324     {
325       g_propagate_prefixed_error (
326         error, nested_error, "Failed to emit list start: ");
327       return FALSE;
328     }
329 
330   for (int i = 0; i < numentries; i++)
331     {
332       ret = mmd_emitter_scalar (
333         emitter, list[i], YAML_PLAIN_SCALAR_STYLE, &nested_error);
334       if (!ret)
335         {
336           g_propagate_prefixed_error (
337             error, nested_error, "Failed to emit list entry: ");
338           return FALSE;
339         }
340     }
341 
342   ret = mmd_emitter_end_sequence (emitter, &nested_error);
343   if (!ret)
344     {
345       g_propagate_prefixed_error (
346         error, nested_error, "Failed to emit list end: ");
347       return FALSE;
348     }
349   return TRUE;
350 }
351 
352 
353 GDate *
modulemd_yaml_parse_date(yaml_parser_t * parser,GError ** error)354 modulemd_yaml_parse_date (yaml_parser_t *parser, GError **error)
355 {
356   MMD_INIT_YAML_EVENT (event);
357   g_auto (GStrv) strv = NULL;
358 
359   YAML_PARSER_PARSE_WITH_EXIT (parser, &event, error);
360   if (event.type != YAML_SCALAR_EVENT)
361     {
362       MMD_YAML_ERROR_EVENT_EXIT (error, event, "Date was not a scalar");
363     }
364 
365   g_debug ("Parsing scalar: %s", (const gchar *)event.data.scalar.value);
366 
367   strv = g_strsplit ((const gchar *)event.data.scalar.value, "-", 4);
368 
369   if (!strv[0] || !strv[1] || !strv[2])
370     {
371       MMD_YAML_ERROR_EVENT_EXIT (
372         error, event, "Date not in the form YYYY-MM-DD");
373     }
374 
375   return g_date_new_dmy (g_ascii_strtoull (strv[2], NULL, 10), /* Day */
376                          g_ascii_strtoull (strv[1], NULL, 10), /* Month */
377                          g_ascii_strtoull (strv[0], NULL, 10)); /* Year */
378 }
379 
380 gchar *
modulemd_yaml_parse_string(yaml_parser_t * parser,GError ** error)381 modulemd_yaml_parse_string (yaml_parser_t *parser, GError **error)
382 {
383   MMD_INIT_YAML_EVENT (event);
384 
385   YAML_PARSER_PARSE_WITH_EXIT (parser, &event, error);
386   if (event.type != YAML_SCALAR_EVENT)
387     {
388       MMD_YAML_ERROR_EVENT_EXIT (error, event, "String was not a scalar");
389     }
390 
391   g_debug ("Parsing scalar: %s", (const gchar *)event.data.scalar.value);
392 
393   return g_strdup ((const gchar *)event.data.scalar.value);
394 }
395 
396 
397 gboolean
modulemd_yaml_parse_bool(yaml_parser_t * parser,GError ** error)398 modulemd_yaml_parse_bool (yaml_parser_t *parser, GError **error)
399 {
400   MMD_INIT_YAML_EVENT (event);
401 
402   YAML_PARSER_PARSE_WITH_EXIT_BOOL (parser, &event, error);
403   if (event.type != YAML_SCALAR_EVENT)
404     {
405       MMD_YAML_ERROR_EVENT_EXIT_INT (
406         error, event, "Expected a scalar boolean");
407     }
408 
409   if (g_str_equal ((const gchar *)event.data.scalar.value, "false"))
410     {
411       return FALSE;
412     }
413   if (g_str_equal ((const gchar *)event.data.scalar.value, "true"))
414     {
415       return TRUE;
416     }
417 
418   MMD_YAML_ERROR_EVENT_EXIT_INT (
419     error,
420     event,
421     "Boolean value was neither \"true\" nor \"false\": %s",
422     (const gchar *)event.data.scalar.value);
423 }
424 
425 
426 gint64
modulemd_yaml_parse_int64(yaml_parser_t * parser,GError ** error)427 modulemd_yaml_parse_int64 (yaml_parser_t *parser, GError **error)
428 {
429   gint64 value;
430   gchar *endptr;
431   MMD_INIT_YAML_EVENT (event);
432 
433   YAML_PARSER_PARSE_WITH_EXIT_INT (parser, &event, error);
434   if (event.type != YAML_SCALAR_EVENT)
435     {
436       MMD_YAML_ERROR_EVENT_EXIT_INT (error, event, "String was not a scalar");
437     }
438 
439   value =
440     g_ascii_strtoll ((const gchar *)event.data.scalar.value, &endptr, 10);
441 
442   if ((value == G_MAXINT64 && errno == ERANGE))
443     {
444       g_set_error (error,
445                    MODULEMD_YAML_ERROR,
446                    MODULEMD_ERROR_VALIDATE,
447                    "%s: The integer value is larger than %" G_GINT64_FORMAT,
448                    (const gchar *)event.data.scalar.value,
449                    G_MAXINT64);
450       return 0;
451     }
452 
453   if ((value == G_MININT64 && errno == ERANGE))
454     {
455       g_set_error (error,
456                    MODULEMD_YAML_ERROR,
457                    MODULEMD_ERROR_VALIDATE,
458                    "%s: The integer value is samller than %" G_GINT64_FORMAT,
459                    (const gchar *)event.data.scalar.value,
460                    G_MININT64);
461       return 0;
462     }
463 
464   if (value == 0 && errno == EINVAL)
465     {
466       g_set_error_literal (
467         error,
468         MODULEMD_YAML_ERROR,
469         MODULEMD_ERROR_NOT_IMPLEMENTED,
470         "Your GLib library does not support parsing integers in 10 base");
471       return 0;
472     }
473 
474   if ((value == 0 && endptr == (gchar *)event.data.scalar.value) ||
475       *endptr != '\0')
476     {
477       g_set_error (error,
478                    MODULEMD_YAML_ERROR,
479                    MMD_ERROR_VALIDATE,
480                    "%s: The string is not a valid integer",
481                    (const gchar *)event.data.scalar.value);
482       return 0;
483     }
484 
485   return value;
486 }
487 
488 
489 guint64
modulemd_yaml_parse_uint64(yaml_parser_t * parser,GError ** error)490 modulemd_yaml_parse_uint64 (yaml_parser_t *parser, GError **error)
491 {
492   guint64 value;
493   gchar *endptr;
494   MMD_INIT_YAML_EVENT (event);
495 
496   YAML_PARSER_PARSE_WITH_EXIT_INT (parser, &event, error);
497   if (event.type != YAML_SCALAR_EVENT)
498     {
499       MMD_YAML_ERROR_EVENT_EXIT_INT (error, event, "String was not a scalar");
500     }
501 
502   g_debug ("Parsing scalar: %s", (const gchar *)event.data.scalar.value);
503 
504   /* g_ascii_strtoull() accepts negative values by definition. */
505   if (event.data.scalar.value[0] == '-')
506     {
507       g_set_error (error,
508                    MODULEMD_YAML_ERROR,
509                    MODULEMD_ERROR_VALIDATE,
510                    "%s: The integer value is negative",
511                    (const gchar *)event.data.scalar.value);
512       return 0u;
513     }
514 
515   value =
516     g_ascii_strtoull ((const gchar *)event.data.scalar.value, &endptr, 10);
517 
518   if (value == G_MAXUINT64 && errno == ERANGE)
519     {
520       g_set_error (error,
521                    MODULEMD_YAML_ERROR,
522                    MODULEMD_ERROR_VALIDATE,
523                    "%s: The integer value is larger than %" G_GUINT64_FORMAT,
524                    (const gchar *)event.data.scalar.value,
525                    G_MAXUINT64);
526       return 0u;
527     }
528 
529   if (value == 0u && errno == EINVAL)
530     {
531       g_set_error_literal (
532         error,
533         MODULEMD_YAML_ERROR,
534         MODULEMD_ERROR_NOT_IMPLEMENTED,
535         "Your GLib library does not support parsing integers in 10 base");
536       return 0u;
537     }
538 
539   if ((value == 0u && endptr == (gchar *)event.data.scalar.value) ||
540       *endptr != '\0')
541     {
542       g_set_error (error,
543                    MODULEMD_YAML_ERROR,
544                    MMD_ERROR_VALIDATE,
545                    "%s: The string is not a valid integer",
546                    (const gchar *)event.data.scalar.value);
547       return 0u;
548     }
549 
550   return value;
551 }
552 
553 
554 GHashTable *
modulemd_yaml_parse_string_set(yaml_parser_t * parser,GError ** error)555 modulemd_yaml_parse_string_set (yaml_parser_t *parser, GError **error)
556 {
557   MMD_INIT_YAML_EVENT (event);
558   gboolean done = FALSE;
559   gboolean in_list = FALSE;
560   g_autoptr (GHashTable) result =
561     g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
562 
563   while (!done)
564     {
565       YAML_PARSER_PARSE_WITH_EXIT (parser, &event, error);
566 
567       switch (event.type)
568         {
569         case YAML_SEQUENCE_START_EVENT: in_list = TRUE; break;
570 
571         case YAML_SEQUENCE_END_EVENT:
572           if (!in_list)
573             {
574               MMD_YAML_ERROR_EVENT_EXIT (
575                 error, event, "Unexpected end of list");
576             }
577           in_list = FALSE;
578           done = TRUE;
579           break;
580 
581         case YAML_SCALAR_EVENT:
582           g_debug ("Parsing scalar: %s",
583                    (const gchar *)event.data.scalar.value);
584           g_hash_table_add (result,
585                             g_strdup ((const gchar *)event.data.scalar.value));
586 
587           if (!in_list)
588             {
589               /* We got a scalar instead of a sequence. Treat it as a list with
590                * a single entry
591                */
592               done = TRUE;
593             }
594           break;
595 
596         default:
597           MMD_YAML_ERROR_EVENT_EXIT (
598             error, event, "Unexpected YAML event in list");
599           break;
600         }
601       yaml_event_delete (&event);
602     }
603 
604   /* Work around false-positive in clang static analysis which thinks it's
605    * possible for this function to return NULL and not set error.
606    */
607   if (G_UNLIKELY (result == NULL))
608     {
609       g_set_error (error,
610                    MODULEMD_YAML_ERROR,
611                    MMD_YAML_ERROR_EMIT,
612                    "Somehow got a NULL hash table here.");
613     }
614 
615   return g_steal_pointer (&result);
616 }
617 
618 
619 GHashTable *
modulemd_yaml_parse_string_set_from_map(yaml_parser_t * parser,const gchar * key,gboolean strict,GError ** error)620 modulemd_yaml_parse_string_set_from_map (yaml_parser_t *parser,
621                                          const gchar *key,
622                                          gboolean strict,
623                                          GError **error)
624 {
625   MMD_INIT_YAML_EVENT (event);
626   gboolean done = FALSE;
627   gboolean in_map = FALSE;
628   g_autoptr (GHashTable) set = NULL;
629   g_autoptr (GError) nested_error = NULL;
630 
631   while (!done)
632     {
633       YAML_PARSER_PARSE_WITH_EXIT (parser, &event, error);
634 
635       switch (event.type)
636         {
637         case YAML_MAPPING_START_EVENT: in_map = TRUE; break;
638 
639         case YAML_MAPPING_END_EVENT:
640           if (!in_map)
641             {
642               MMD_YAML_ERROR_EVENT_EXIT (
643                 error, event, "Unexpected end of map");
644             }
645           in_map = FALSE;
646           done = TRUE;
647           break;
648 
649         case YAML_SCALAR_EVENT:
650           if (!in_map)
651             {
652               MMD_YAML_ERROR_EVENT_EXIT (
653                 error, event, "Unexpected scalar outside of map.");
654             }
655 
656           if (g_str_equal ((const gchar *)event.data.scalar.value, key))
657             {
658               set = modulemd_yaml_parse_string_set (parser, &nested_error);
659               if (!set)
660                 {
661                   g_propagate_error (error, g_steal_pointer (&nested_error));
662                   return NULL;
663                 }
664             }
665           else
666             {
667               /* Encountered a key other than the expected one. */
668               SKIP_UNKNOWN (parser,
669                             NULL,
670                             "Unexpected key in map: %s",
671                             (const gchar *)event.data.scalar.value);
672             }
673           break;
674 
675         default:
676           MMD_YAML_ERROR_EVENT_EXIT (
677             error, event, "Unexpected YAML event in map");
678           break;
679         }
680       yaml_event_delete (&event);
681     }
682   return g_steal_pointer (&set);
683 }
684 
685 
686 GHashTable *
modulemd_yaml_parse_string_string_map(yaml_parser_t * parser,GError ** error)687 modulemd_yaml_parse_string_string_map (yaml_parser_t *parser, GError **error)
688 {
689   MMD_INIT_YAML_EVENT (event);
690   gboolean done = FALSE;
691   g_autoptr (GHashTable) table = NULL;
692   g_autoptr (GError) nested_error = NULL;
693   const gchar *key = NULL;
694   g_autofree gchar *value = NULL;
695 
696   table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
697 
698   YAML_PARSER_PARSE_WITH_EXIT (parser, &event, error);
699   if (event.type != YAML_MAPPING_START_EVENT)
700     {
701       MMD_YAML_ERROR_EVENT_EXIT (error,
702                                  event,
703                                  "Got %s instead of MAPPING_START.",
704                                  mmd_yaml_get_event_name (event.type));
705     }
706 
707   while (!done)
708     {
709       YAML_PARSER_PARSE_WITH_EXIT (parser, &event, error);
710 
711       switch (event.type)
712         {
713         case YAML_MAPPING_END_EVENT: done = TRUE; break;
714 
715         case YAML_SCALAR_EVENT:
716           /* Parse the value */
717           key = (const gchar *)event.data.scalar.value;
718 
719           value = modulemd_yaml_parse_string (parser, &nested_error);
720           if (!value)
721             {
722               g_propagate_error (error, g_steal_pointer (&nested_error));
723               return NULL;
724             }
725 
726           g_hash_table_replace (
727             table, g_strdup (key), g_steal_pointer (&value));
728 
729           break;
730 
731         default:
732           MMD_YAML_ERROR_EVENT_EXIT (
733             error, event, "Unexpected YAML event in map");
734           break;
735         }
736       yaml_event_delete (&event);
737     }
738   return g_steal_pointer (&table);
739 }
740 
741 
742 GHashTable *
modulemd_yaml_parse_nested_set(yaml_parser_t * parser,GError ** error)743 modulemd_yaml_parse_nested_set (yaml_parser_t *parser, GError **error)
744 {
745   MODULEMD_INIT_TRACE ();
746   MMD_INIT_YAML_EVENT (event);
747   gboolean done = FALSE;
748   g_autofree gchar *key = NULL;
749   g_autoptr (GHashTable) value = NULL;
750   g_autoptr (GHashTable) t = NULL;
751   g_autoptr (GError) nested_error = NULL;
752 
753   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
754 
755   t = g_hash_table_new_full (
756     g_str_hash, g_str_equal, g_free, (GDestroyNotify)g_hash_table_unref);
757 
758   /* The first event must be a MAPPING_START */
759   YAML_PARSER_PARSE_WITH_EXIT (parser, &event, error);
760   if (event.type != YAML_MAPPING_START_EVENT)
761     {
762       MMD_YAML_ERROR_EVENT_EXIT (
763         error, event, "Missing mapping in nested set");
764     }
765 
766   while (!done)
767     {
768       YAML_PARSER_PARSE_WITH_EXIT (parser, &event, error);
769 
770       switch (event.type)
771         {
772         case YAML_MAPPING_END_EVENT: done = TRUE; break;
773 
774         case YAML_SCALAR_EVENT:
775           key = g_strdup ((const gchar *)event.data.scalar.value);
776           if (g_hash_table_contains (t,
777                                      (const gchar *)event.data.scalar.value))
778             {
779               MMD_YAML_ERROR_EVENT_EXIT (
780                 error,
781                 event,
782                 "Key %s encountered twice in nested set",
783                 (const gchar *)event.data.scalar.value);
784             }
785 
786           value = modulemd_yaml_parse_string_set (parser, &nested_error);
787           if (value == NULL)
788             {
789               MMD_YAML_ERROR_EVENT_EXIT (error,
790                                          event,
791                                          "Failed to parse nested set: %s",
792                                          nested_error->message);
793             }
794 
795           g_hash_table_insert (
796             t, g_steal_pointer (&key), g_steal_pointer (&value));
797           break;
798 
799         default:
800           MMD_YAML_ERROR_EVENT_EXIT (error,
801                                      event,
802                                      "Unexpected YAML event in nested set: %d",
803                                      event.type);
804           break;
805         }
806       yaml_event_delete (&event);
807     }
808 
809   /* Work around false-positive in clang static analysis which thinks it's
810    * possible for this function to return NULL and not set error.
811    */
812   if (G_UNLIKELY (t == NULL))
813     {
814       g_set_error (error,
815                    MODULEMD_YAML_ERROR,
816                    MMD_YAML_ERROR_EMIT,
817                    "Somehow got a NULL hash table here.");
818     }
819 
820   return g_steal_pointer (&t);
821 }
822 
823 gboolean
modulemd_yaml_emit_nested_set(yaml_emitter_t * emitter,GHashTable * table,GError ** error)824 modulemd_yaml_emit_nested_set (yaml_emitter_t *emitter,
825                                GHashTable *table,
826                                GError **error)
827 {
828   MODULEMD_INIT_TRACE ();
829   int ret;
830   g_autoptr (GError) nested_error = NULL;
831   MMD_INIT_YAML_EVENT (event);
832   g_autoptr (GPtrArray) keys = NULL;
833   GHashTable *dep = NULL;
834   gchar *key = NULL;
835 
836   ret = mmd_emitter_start_mapping (
837     emitter, YAML_BLOCK_MAPPING_STYLE, &nested_error);
838   if (!ret)
839     {
840       g_propagate_prefixed_error (
841         error,
842         g_steal_pointer (&nested_error),
843         "Failed to start dependencies nested mapping: ");
844       return FALSE;
845     }
846 
847   keys = modulemd_ordered_str_keys (table, modulemd_strcmp_sort);
848   for (gint i = 0; i < keys->len; i++)
849     {
850       key = g_ptr_array_index (keys, i);
851       dep = g_hash_table_lookup (table, key);
852 
853       EMIT_STRING_SET_FULL (
854         emitter, error, key, dep, YAML_FLOW_SEQUENCE_STYLE);
855     }
856 
857   ret = mmd_emitter_end_mapping (emitter, &nested_error);
858   if (!ret)
859     {
860       g_propagate_prefixed_error (error,
861                                   g_steal_pointer (&nested_error),
862                                   "Failed to end nested mapping");
863       return FALSE;
864     }
865 
866   return TRUE;
867 }
868 
869 
870 static gboolean
modulemd_yaml_parse_document_type_internal(yaml_parser_t * parser,ModulemdYamlDocumentTypeEnum * _doctype,guint64 * _mdversion,yaml_emitter_t * emitter,GError ** error)871 modulemd_yaml_parse_document_type_internal (
872   yaml_parser_t *parser,
873   ModulemdYamlDocumentTypeEnum *_doctype,
874   guint64 *_mdversion,
875   yaml_emitter_t *emitter,
876   GError **error)
877 {
878   MODULEMD_INIT_TRACE ();
879   MMD_INIT_YAML_EVENT (event);
880   gboolean done = FALSE;
881   gboolean had_data = FALSE;
882   ModulemdYamlDocumentTypeEnum doctype = MODULEMD_YAML_DOC_UNKNOWN;
883   guint64 mdversion = 0;
884   g_autofree gchar *doctype_scalar = NULL;
885   g_autofree gchar *mdversion_string = NULL;
886   g_autoptr (GError) nested_error = NULL;
887   int depth = 0;
888 
889   if (!mmd_emitter_start_stream (emitter, &nested_error))
890     {
891       g_propagate_prefixed_error (
892         error, nested_error, "Error emitting stream: ");
893       return FALSE;
894     }
895 
896   /*
897    * We should assume the initial document start is consumed by the Index.
898    * But we still emit it.
899    */
900   if (!mmd_emitter_start_document (emitter, error))
901     {
902       return FALSE;
903     }
904 
905   /* The second event must be the mapping start */
906   YAML_PARSER_PARSE_WITH_EXIT_BOOL (parser, &event, error);
907   if (event.type != YAML_MAPPING_START_EVENT)
908     {
909       MMD_YAML_ERROR_EVENT_EXIT_BOOL (
910         error, event, "Document did not start with a mapping");
911     }
912   MMD_EMIT_WITH_EXIT_FULL (
913     emitter, FALSE, &event, error, "Error starting mapping");
914   yaml_event_delete (&event);
915   depth++;
916 
917   /* Now process through the document top-level */
918   while (!done)
919     {
920       YAML_PARSER_PARSE_WITH_EXIT_BOOL (parser, &event, error);
921 
922       switch (event.type)
923         {
924         case YAML_MAPPING_END_EVENT:
925           if (!mmd_emitter_end_mapping (emitter, error))
926             {
927               return FALSE;
928             }
929           depth--;
930           if (depth == 0)
931             {
932               done = TRUE;
933             }
934           break;
935 
936         case YAML_MAPPING_START_EVENT:
937           if (!mmd_emitter_start_mapping (
938                 emitter, event.data.mapping_start.style, error))
939             {
940               return FALSE;
941             }
942           depth++;
943           break;
944 
945         case YAML_SCALAR_EVENT:
946           if (!mmd_emitter_scalar (emitter,
947                                    (const gchar *)event.data.scalar.value,
948                                    event.data.scalar.style,
949                                    error))
950             {
951               return FALSE;
952             }
953 
954           if (depth == 1 && g_str_equal (event.data.scalar.value, "document"))
955             {
956               if (doctype != MODULEMD_YAML_DOC_UNKNOWN)
957                 {
958                   MMD_YAML_ERROR_EVENT_EXIT_BOOL (
959                     error, event, "Document type encountered twice.");
960                 }
961 
962               doctype_scalar =
963                 modulemd_yaml_parse_string (parser, &nested_error);
964               if (!doctype_scalar)
965                 {
966                   g_propagate_error (error, g_steal_pointer (&nested_error));
967                   return FALSE;
968                 }
969               if (!mmd_emitter_scalar (emitter,
970                                        (const gchar *)doctype_scalar,
971                                        YAML_PLAIN_SCALAR_STYLE,
972                                        error))
973                 {
974                   return FALSE;
975                 }
976 
977               if (g_str_equal (doctype_scalar, "modulemd") ||
978                   g_str_equal (doctype_scalar, "modulemd-stream"))
979                 {
980                   doctype = MODULEMD_YAML_DOC_MODULESTREAM;
981                 }
982               else if (g_str_equal (doctype_scalar, "modulemd-defaults"))
983                 {
984                   doctype = MODULEMD_YAML_DOC_DEFAULTS;
985                 }
986               else if (g_str_equal (doctype_scalar, "modulemd-translations"))
987                 {
988                   doctype = MODULEMD_YAML_DOC_TRANSLATIONS;
989                 }
990               else if (g_str_equal (doctype_scalar, "modulemd-packager"))
991                 {
992                   doctype = MODULEMD_YAML_DOC_PACKAGER;
993                 }
994               else if (g_str_equal (doctype_scalar, "modulemd-obsoletes"))
995                 {
996                   doctype = MODULEMD_YAML_DOC_OBSOLETES;
997                 }
998               else
999                 {
1000                   MMD_YAML_ERROR_EVENT_EXIT_BOOL (
1001                     error, event, "Document type %s unknown.", doctype_scalar);
1002                 }
1003 
1004               g_clear_pointer (&doctype_scalar, g_free);
1005             }
1006           else if (depth == 1 &&
1007                    g_str_equal (event.data.scalar.value, "version"))
1008             {
1009               if (mdversion != 0)
1010                 {
1011                   MMD_YAML_ERROR_EVENT_EXIT_BOOL (
1012                     error, event, "Metadata version encountered twice.");
1013                 }
1014 
1015               mdversion = modulemd_yaml_parse_uint64 (parser, &nested_error);
1016               if (nested_error)
1017                 {
1018                   /* If we got a parsing error, report it. Otherwise, continue
1019                    * and we'll catch the invalid mdversion further on
1020                    */
1021                   g_propagate_error (error, g_steal_pointer (&nested_error));
1022                   return FALSE;
1023                 }
1024               mdversion_string = g_strdup_printf ("%" PRIu64, mdversion);
1025               if (!mmd_emitter_scalar (
1026                     emitter, mdversion_string, YAML_PLAIN_SCALAR_STYLE, error))
1027                 {
1028                   return FALSE;
1029                 }
1030             }
1031           else if (depth == 1 && g_str_equal (event.data.scalar.value, "data"))
1032             {
1033               had_data = TRUE;
1034             }
1035 
1036           break;
1037 
1038         default:
1039           /* Anything else, we just re-emit into the subdocument */
1040           MMD_EMIT_WITH_EXIT_FULL (
1041             emitter, FALSE, &event, error, "Error re-emiting event");
1042           ;
1043           break;
1044         }
1045 
1046       yaml_event_delete (&event);
1047     }
1048 
1049   /* The final event must be the document end */
1050   YAML_PARSER_PARSE_WITH_EXIT_BOOL (parser, &event, error);
1051   if (event.type != YAML_DOCUMENT_END_EVENT)
1052     {
1053       MMD_YAML_ERROR_EVENT_EXIT_BOOL (
1054         error, event, "Document did not end. It just goes on forever...");
1055     }
1056   MMD_EMIT_WITH_EXIT_FULL (
1057     emitter, FALSE, &event, error, "Error ending document");
1058   yaml_event_delete (&event);
1059 
1060   if (!mmd_emitter_end_stream (emitter, error))
1061     {
1062       return FALSE;
1063     }
1064 
1065   if (doctype == MODULEMD_YAML_DOC_UNKNOWN)
1066     {
1067       g_set_error_literal (error,
1068                            MODULEMD_YAML_ERROR,
1069                            MMD_YAML_ERROR_MISSING_REQUIRED,
1070                            "No document type specified");
1071       return FALSE;
1072     }
1073 
1074   if (!mdversion)
1075     {
1076       g_set_error_literal (error,
1077                            MODULEMD_YAML_ERROR,
1078                            MMD_YAML_ERROR_MISSING_REQUIRED,
1079                            "No metadata version specified");
1080       return FALSE;
1081     }
1082 
1083   if (doctype == MODULEMD_YAML_DOC_PACKAGER && mdversion < 2)
1084     {
1085       g_set_error_literal (error,
1086                            MODULEMD_YAML_ERROR,
1087                            MMD_YAML_ERROR_INCONSISTENT,
1088                            "Document type 'modulemd-packager' is permissible "
1089                            "only for module stream version 2 or higher.");
1090       return FALSE;
1091     }
1092 
1093   if (!had_data)
1094     {
1095       g_set_error_literal (error,
1096                            MODULEMD_YAML_ERROR,
1097                            MMD_YAML_ERROR_MISSING_REQUIRED,
1098                            "No data section provided");
1099       return FALSE;
1100     }
1101 
1102   *_doctype = doctype;
1103   *_mdversion = mdversion;
1104 
1105   return TRUE;
1106 }
1107 
1108 
1109 ModulemdSubdocumentInfo *
modulemd_yaml_parse_document_type(yaml_parser_t * parser)1110 modulemd_yaml_parse_document_type (yaml_parser_t *parser)
1111 {
1112   MMD_INIT_YAML_EMITTER (emitter);
1113   MMD_INIT_YAML_STRING (&emitter, yaml_string);
1114   g_autoptr (ModulemdSubdocumentInfo) s = modulemd_subdocument_info_new ();
1115   ModulemdYamlDocumentTypeEnum doctype = MODULEMD_YAML_DOC_UNKNOWN;
1116   guint64 mdversion = 0;
1117   g_autoptr (GError) error = NULL;
1118 
1119   if (!modulemd_yaml_parse_document_type_internal (
1120         parser, &doctype, &mdversion, &emitter, &error))
1121     {
1122       modulemd_subdocument_info_set_gerror (s, error);
1123     }
1124 
1125   modulemd_subdocument_info_set_doctype (s, doctype);
1126   modulemd_subdocument_info_set_mdversion (s, mdversion);
1127   modulemd_subdocument_info_set_yaml (s, yaml_string->str);
1128 
1129   return g_steal_pointer (&s);
1130 }
1131 
1132 
1133 static const gchar *
modulemd_yaml_get_doctype_string(ModulemdYamlDocumentTypeEnum doctype,guint64 mdversion)1134 modulemd_yaml_get_doctype_string (ModulemdYamlDocumentTypeEnum doctype,
1135                                   guint64 mdversion)
1136 {
1137   switch (doctype)
1138     {
1139     case MODULEMD_YAML_DOC_MODULESTREAM:
1140       if (mdversion <= 2)
1141         {
1142           return "modulemd";
1143         }
1144       return "modulemd-stream";
1145 
1146     case MODULEMD_YAML_DOC_DEFAULTS: return "modulemd-defaults";
1147 
1148     case MODULEMD_YAML_DOC_TRANSLATIONS: return "modulemd-translations";
1149 
1150     case MODULEMD_YAML_DOC_OBSOLETES: return "modulemd-obsoletes";
1151 
1152     case MODULEMD_YAML_DOC_PACKAGER: return "modulemd-packager";
1153 
1154     default: return NULL;
1155     }
1156 }
1157 
1158 
1159 gboolean
modulemd_yaml_emit_document_headers(yaml_emitter_t * emitter,ModulemdYamlDocumentTypeEnum doctype,guint64 mdversion,GError ** error)1160 modulemd_yaml_emit_document_headers (yaml_emitter_t *emitter,
1161                                      ModulemdYamlDocumentTypeEnum doctype,
1162                                      guint64 mdversion,
1163                                      GError **error)
1164 {
1165   MODULEMD_INIT_TRACE ();
1166   const gchar *doctype_string =
1167     modulemd_yaml_get_doctype_string (doctype, mdversion);
1168   g_autofree gchar *mdversion_string = g_strdup_printf ("%" PRIu64, mdversion);
1169 
1170   if (!mmd_emitter_start_document (emitter, error))
1171     {
1172       return FALSE;
1173     }
1174 
1175   if (!mmd_emitter_start_mapping (emitter, YAML_BLOCK_MAPPING_STYLE, error))
1176     {
1177       return FALSE;
1178     }
1179 
1180   if (!mmd_emitter_scalar (
1181         emitter, "document", YAML_PLAIN_SCALAR_STYLE, error))
1182     {
1183       return FALSE;
1184     }
1185 
1186   if (!mmd_emitter_scalar (
1187         emitter, doctype_string, YAML_PLAIN_SCALAR_STYLE, error))
1188     {
1189       return FALSE;
1190     }
1191 
1192   if (!mmd_emitter_scalar (emitter, "version", YAML_PLAIN_SCALAR_STYLE, error))
1193     {
1194       return FALSE;
1195     }
1196 
1197   if (!mmd_emitter_scalar (
1198         emitter, mdversion_string, YAML_PLAIN_SCALAR_STYLE, error))
1199     {
1200       return FALSE;
1201     }
1202 
1203   if (!mmd_emitter_scalar (emitter, "data", YAML_PLAIN_SCALAR_STYLE, error))
1204     {
1205       return FALSE;
1206     }
1207 
1208   return TRUE;
1209 }
1210 
1211 
1212 gboolean
modulemd_yaml_emit_variant(yaml_emitter_t * emitter,GVariant * variant,GError ** error)1213 modulemd_yaml_emit_variant (yaml_emitter_t *emitter,
1214                             GVariant *variant,
1215                             GError **error)
1216 {
1217   GVariantIter iter;
1218   g_autofree gchar *key = NULL;
1219   g_autoptr (GVariant) value = NULL;
1220   g_autoptr (GPtrArray) keys = NULL;
1221   g_autoptr (GVariantDict) dict = NULL;
1222 
1223   if (g_variant_is_of_type (variant, G_VARIANT_TYPE_STRING))
1224     {
1225       EMIT_SCALAR (emitter, error, g_variant_get_string (variant, NULL));
1226     }
1227   else if (g_variant_is_of_type (variant, G_VARIANT_TYPE_BOOLEAN))
1228     {
1229       if (g_variant_get_boolean (variant))
1230         {
1231           EMIT_SCALAR (emitter, error, "TRUE");
1232         }
1233       else
1234         {
1235           EMIT_SCALAR (emitter, error, "FALSE");
1236         }
1237     }
1238   else if (g_variant_is_of_type (variant, G_VARIANT_TYPE_DICTIONARY))
1239     {
1240       EMIT_MAPPING_START (emitter, error);
1241       keys = g_ptr_array_new_with_free_func (g_free);
1242 
1243       dict = g_variant_dict_new (variant);
1244       g_variant_iter_init (&iter, variant);
1245 
1246       /* Get a list of the keys to sort */
1247       while (g_variant_iter_next (&iter, "{sv}", &key, &value))
1248         {
1249           g_ptr_array_add (keys, g_steal_pointer (&key));
1250           g_clear_pointer (&value, g_variant_unref);
1251         }
1252 
1253       /* Sort the keys alphabetically */
1254       g_ptr_array_sort (keys, modulemd_strcmp_sort);
1255 
1256       /* Write out the keys and recurse into their values */
1257       for (guint i = 0; i < keys->len; i++)
1258         {
1259           value = g_variant_dict_lookup_value (
1260             dict, g_ptr_array_index (keys, i), NULL);
1261           if (!value)
1262             {
1263               g_set_error (
1264                 error,
1265                 MODULEMD_YAML_ERROR,
1266                 MMD_YAML_ERROR_EMIT,
1267                 "Got unexpected type while processing XMD dictionary.");
1268               return FALSE;
1269             }
1270           EMIT_SCALAR (emitter, error, g_ptr_array_index (keys, i));
1271           if (!modulemd_yaml_emit_variant (emitter, value, error))
1272             {
1273               return FALSE;
1274             }
1275 
1276           g_clear_pointer (&value, g_variant_unref);
1277         }
1278 
1279       g_clear_pointer (&keys, g_ptr_array_unref);
1280       EMIT_MAPPING_END (emitter, error);
1281     }
1282   else if (g_variant_is_of_type (variant, G_VARIANT_TYPE_ARRAY))
1283     {
1284       EMIT_SEQUENCE_START (emitter, error);
1285       g_variant_iter_init (&iter, variant);
1286       while ((value = g_variant_iter_next_value (&iter)))
1287         {
1288           if (!modulemd_yaml_emit_variant (emitter, value, error))
1289             {
1290               return FALSE;
1291             }
1292           g_clear_pointer (&value, g_variant_unref);
1293         }
1294       EMIT_SEQUENCE_END (emitter, error);
1295     }
1296   else
1297     {
1298       g_set_error (error,
1299                    MODULEMD_YAML_ERROR,
1300                    MMD_YAML_ERROR_EMIT,
1301                    "Unhandled variant type: \"%s\": %s",
1302                    g_variant_get_type_string (variant),
1303                    g_variant_print (variant, TRUE));
1304       return FALSE;
1305     }
1306   return TRUE;
1307 }
1308 
1309 GVariant *
mmd_variant_from_scalar(const gchar * scalar)1310 mmd_variant_from_scalar (const gchar *scalar)
1311 {
1312   MODULEMD_INIT_TRACE ();
1313   GVariant *variant = NULL;
1314 
1315   g_debug ("Variant from scalar: %s", scalar);
1316 
1317   g_return_val_if_fail (scalar, NULL);
1318 
1319   /* Treat "TRUE" and "FALSE" as boolean values */
1320   if (g_str_equal (scalar, "TRUE"))
1321     {
1322       variant = g_variant_new_boolean (TRUE);
1323     }
1324   else if (g_str_equal (scalar, "FALSE"))
1325     {
1326       variant = g_variant_new_boolean (FALSE);
1327     }
1328 
1329   else if (scalar)
1330     {
1331       /* Any value we don't handle specifically becomes a string */
1332       variant = g_variant_new_string (scalar);
1333     }
1334 
1335   return variant;
1336 }
1337 
1338 
1339 GVariant *
mmd_variant_from_mapping(yaml_parser_t * parser,GError ** error)1340 mmd_variant_from_mapping (yaml_parser_t *parser, GError **error)
1341 {
1342   MODULEMD_INIT_TRACE ();
1343   gboolean done = FALSE;
1344   MMD_INIT_YAML_EVENT (event);
1345   MMD_INIT_YAML_EVENT (value_event);
1346 
1347   g_autoptr (GVariantDict) dict = NULL;
1348   g_autoptr (GVariant) value = NULL;
1349   g_autofree gchar *key = NULL;
1350   g_autoptr (GError) nested_error = NULL;
1351 
1352   dict = g_variant_dict_new (NULL);
1353 
1354   while (!done)
1355     {
1356       YAML_PARSER_PARSE_WITH_EXIT (parser, &event, error);
1357 
1358       switch (event.type)
1359         {
1360         case YAML_MAPPING_END_EVENT:
1361           /* We've processed the whole dictionary */
1362           done = TRUE;
1363           break;
1364 
1365         case YAML_SCALAR_EVENT:
1366           /* All mapping keys must be scalars */
1367           key = g_strdup ((const gchar *)event.data.scalar.value);
1368 
1369           YAML_PARSER_PARSE_WITH_EXIT (parser, &value_event, error);
1370 
1371           switch (value_event.type)
1372             {
1373             case YAML_SCALAR_EVENT:
1374               value = mmd_variant_from_scalar (
1375                 (const gchar *)value_event.data.scalar.value);
1376               if (!value)
1377                 {
1378                   MMD_YAML_ERROR_EVENT_EXIT (
1379                     error, event, "Error parsing scalar");
1380                 }
1381               break;
1382 
1383             case YAML_MAPPING_START_EVENT:
1384               value = mmd_variant_from_mapping (parser, &nested_error);
1385               if (!value)
1386                 {
1387                   g_propagate_error (error, g_steal_pointer (&nested_error));
1388                   return NULL;
1389                 }
1390               break;
1391 
1392             case YAML_SEQUENCE_START_EVENT:
1393               value = mmd_variant_from_sequence (parser, &nested_error);
1394               if (!value)
1395                 {
1396                   g_propagate_error (error, g_steal_pointer (&nested_error));
1397                   return NULL;
1398                 }
1399               break;
1400 
1401             default:
1402               /* We received a YAML event we shouldn't expect at this level */
1403               MMD_YAML_ERROR_EVENT_EXIT (
1404                 error,
1405                 event,
1406                 "Unexpected YAML event in inner raw mapping: %s",
1407                 mmd_yaml_get_event_name (value_event.type));
1408               break;
1409             }
1410 
1411           yaml_event_delete (&value_event);
1412           g_variant_dict_insert_value (dict, key, g_steal_pointer (&value));
1413           g_clear_pointer (&key, g_free);
1414           break;
1415 
1416         default:
1417           /* We received a YAML event we shouldn't expect at this level */
1418           MMD_YAML_ERROR_EVENT_EXIT (
1419             error,
1420             event,
1421             "Unexpected YAML event in raw mapping: %s",
1422             mmd_yaml_get_event_name (event.type));
1423           break;
1424         }
1425 
1426       yaml_event_delete (&event);
1427     }
1428 
1429   return g_variant_dict_end (dict);
1430 }
1431 
1432 
1433 GVariant *
mmd_variant_from_sequence(yaml_parser_t * parser,GError ** error)1434 mmd_variant_from_sequence (yaml_parser_t *parser, GError **error)
1435 {
1436   MODULEMD_INIT_TRACE ();
1437   gboolean done = FALSE;
1438   MMD_INIT_YAML_EVENT (event);
1439 
1440   g_auto (GVariantBuilder) builder;
1441   g_autoptr (GVariant) value = NULL;
1442   g_autoptr (GError) nested_error = NULL;
1443   gboolean empty_array = TRUE;
1444   GVariant *result = NULL;
1445 
1446   g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
1447 
1448   while (!done)
1449     {
1450       YAML_PARSER_PARSE_WITH_EXIT (parser, &event, error);
1451 
1452       switch (event.type)
1453         {
1454         case YAML_SEQUENCE_END_EVENT:
1455           /* We've processed the whole sequence */
1456           done = TRUE;
1457           break;
1458 
1459         case YAML_SCALAR_EVENT:
1460           value =
1461             mmd_variant_from_scalar ((const gchar *)event.data.scalar.value);
1462           if (!value)
1463             {
1464               MMD_YAML_ERROR_EVENT_EXIT (error, event, "Error parsing scalar");
1465             }
1466           break;
1467 
1468         case YAML_MAPPING_START_EVENT:
1469           value = mmd_variant_from_mapping (parser, &nested_error);
1470           if (!value)
1471             {
1472               g_propagate_error (error, g_steal_pointer (&nested_error));
1473               return NULL;
1474             }
1475           break;
1476 
1477         case YAML_SEQUENCE_START_EVENT:
1478           value = mmd_variant_from_sequence (parser, &nested_error);
1479           if (!value)
1480             {
1481               g_propagate_error (error, g_steal_pointer (&nested_error));
1482               return NULL;
1483             }
1484           break;
1485 
1486         default:
1487           /* We received a YAML event we shouldn't expect at this level */
1488           MMD_YAML_ERROR_EVENT_EXIT (
1489             error,
1490             event,
1491             "Unexpected YAML event in raw sequence: %s",
1492             mmd_yaml_get_event_name (event.type));
1493           break;
1494         }
1495 
1496       if (value)
1497         {
1498           g_variant_builder_add_value (&builder, g_steal_pointer (&value));
1499           empty_array = FALSE;
1500         }
1501 
1502       yaml_event_delete (&event);
1503     }
1504 
1505   if (empty_array)
1506     {
1507       /* If we got an empty array, treat it as a zero-length array of
1508        * GVariants
1509        */
1510       result = g_variant_new ("av", NULL);
1511     }
1512   else
1513     {
1514       /* Otherwise, finish it up */
1515       result = g_variant_builder_end (&builder);
1516     }
1517 
1518   return result;
1519 }
1520 
1521 
1522 GVariant *
mmd_parse_xmd(yaml_parser_t * parser,GError ** error)1523 mmd_parse_xmd (yaml_parser_t *parser, GError **error)
1524 {
1525   MODULEMD_INIT_TRACE ();
1526   MMD_INIT_YAML_EVENT (event);
1527   GVariant *variant = NULL;
1528   g_autoptr (GError) nested_error = NULL;
1529 
1530   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1531 
1532   YAML_PARSER_PARSE_WITH_EXIT (parser, &event, error);
1533 
1534   switch (event.type)
1535     {
1536     case YAML_SCALAR_EVENT:
1537       variant =
1538         mmd_variant_from_scalar ((const gchar *)event.data.scalar.value);
1539       if (!variant)
1540         {
1541           MMD_YAML_ERROR_EVENT_EXIT (error, event, "Error parsing scalar");
1542         }
1543       break;
1544 
1545     case YAML_MAPPING_START_EVENT:
1546       variant = mmd_variant_from_mapping (parser, &nested_error);
1547       break;
1548 
1549     default:
1550       MMD_YAML_ERROR_EVENT_EXIT (error,
1551                                  event,
1552                                  "Unexpected YAML event in raw parsing: %s",
1553                                  mmd_yaml_get_event_name (event.type));
1554       break;
1555     }
1556 
1557   return g_variant_ref_sink (variant);
1558 }
1559 
1560 
1561 static gboolean
1562 skip_unknown_yaml_mapping (yaml_parser_t *parser, GError **error);
1563 static gboolean
1564 skip_unknown_yaml_sequence (yaml_parser_t *parser, GError **error);
1565 
1566 
1567 gboolean
skip_unknown_yaml(yaml_parser_t * parser,GError ** error)1568 skip_unknown_yaml (yaml_parser_t *parser, GError **error)
1569 {
1570   MMD_INIT_YAML_EVENT (event);
1571   MODULEMD_INIT_TRACE ();
1572 
1573   /* This function is called when an unknown key appears in a mapping.
1574    * Read the next event and then skip to the end of it.
1575    */
1576 
1577   YAML_PARSER_PARSE_WITH_EXIT_BOOL (parser, &event, error);
1578 
1579   switch (event.type)
1580     {
1581     case YAML_SCALAR_EVENT:
1582       /* If we get a scalar key, we can just return here */
1583       break;
1584 
1585     case YAML_MAPPING_START_EVENT:
1586       return skip_unknown_yaml_mapping (parser, error);
1587 
1588     case YAML_SEQUENCE_START_EVENT:
1589       return skip_unknown_yaml_sequence (parser, error);
1590 
1591     default:
1592       /* We received a YAML event we shouldn't expect at this level */
1593       g_set_error (error,
1594                    MODULEMD_YAML_ERROR,
1595                    MMD_YAML_ERROR_PARSE,
1596                    "Unexpected YAML event %s in skip_unknown_yaml()",
1597                    mmd_yaml_get_event_name (event.type));
1598       return FALSE;
1599     }
1600 
1601   return TRUE;
1602 }
1603 
1604 
1605 static gboolean
skip_unknown_yaml_sequence(yaml_parser_t * parser,GError ** error)1606 skip_unknown_yaml_sequence (yaml_parser_t *parser, GError **error)
1607 {
1608   MMD_INIT_YAML_EVENT (event);
1609   gsize depth = 0;
1610   gboolean done = FALSE;
1611 
1612   while (!done)
1613     {
1614       YAML_PARSER_PARSE_WITH_EXIT_BOOL (parser, &event, error);
1615 
1616       switch (event.type)
1617         {
1618         case YAML_SCALAR_EVENT: break;
1619 
1620         case YAML_MAPPING_START_EVENT:
1621         case YAML_SEQUENCE_START_EVENT: depth++; break;
1622 
1623         case YAML_MAPPING_END_EVENT: depth--; break;
1624 
1625         case YAML_SEQUENCE_END_EVENT:
1626           if (depth == 0)
1627             {
1628               done = TRUE;
1629               break;
1630             }
1631 
1632           depth--;
1633           break;
1634 
1635 
1636         default:
1637           /* We received a YAML event we shouldn't expect at this level */
1638           g_set_error (
1639             error,
1640             MODULEMD_YAML_ERROR,
1641             MMD_YAML_ERROR_PARSE,
1642             "Unexpected YAML event %s in skip_unknown_yaml_sequence()",
1643             mmd_yaml_get_event_name (event.type));
1644           return FALSE;
1645         }
1646 
1647       yaml_event_delete (&event);
1648     }
1649 
1650   return TRUE;
1651 }
1652 
1653 
1654 static gboolean
skip_unknown_yaml_mapping(yaml_parser_t * parser,GError ** error)1655 skip_unknown_yaml_mapping (yaml_parser_t *parser, GError **error)
1656 {
1657   MMD_INIT_YAML_EVENT (event);
1658   gsize depth = 0;
1659   gboolean done = FALSE;
1660 
1661   while (!done)
1662     {
1663       YAML_PARSER_PARSE_WITH_EXIT_BOOL (parser, &event, error);
1664 
1665       switch (event.type)
1666         {
1667         case YAML_SCALAR_EVENT: break;
1668 
1669         case YAML_MAPPING_START_EVENT:
1670         case YAML_SEQUENCE_START_EVENT: depth++; break;
1671 
1672         case YAML_SEQUENCE_END_EVENT: depth--; break;
1673 
1674         case YAML_MAPPING_END_EVENT:
1675           if (depth == 0)
1676             {
1677               done = TRUE;
1678               break;
1679             }
1680 
1681           depth--;
1682           break;
1683 
1684 
1685         default:
1686           /* We received a YAML event we shouldn't expect at this level */
1687           g_set_error (
1688             error,
1689             MODULEMD_YAML_ERROR,
1690             MMD_YAML_ERROR_PARSE,
1691             "Unexpected YAML event %s in skip_unknown_yaml_sequence()",
1692             mmd_yaml_get_event_name (event.type));
1693           return FALSE;
1694         }
1695 
1696       yaml_event_delete (&event);
1697     }
1698 
1699   return TRUE;
1700 }
1701