1 /*
2 * This file is part of libmodulemd
3 * Copyright (C) 2018 Red Hat, Inc.
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.h"
15 #include "config.h"
16
17 #include "private/modulemd-module-stream-private.h"
18 #include "private/modulemd-subdocument-info-private.h"
19 #include "private/modulemd-packager-v3-private.h"
20
21 #include <errno.h>
22 #include <inttypes.h>
23
24 const gchar *
modulemd_get_version(void)25 modulemd_get_version (void)
26 {
27 return LIBMODULEMD_VERSION;
28 }
29
30
31 static ModulemdModuleIndex *
32 verify_load (int ret,
33 ModulemdModuleIndex *idx,
34 GPtrArray *failures,
35 GError **error,
36 GError **nested_error);
37
38 ModulemdModuleIndex *
modulemd_load_file(const gchar * yaml_file,GError ** error)39 modulemd_load_file (const gchar *yaml_file, GError **error)
40 {
41 gboolean ret;
42 g_autoptr (ModulemdModuleIndex) idx = NULL;
43 g_autoptr (GError) nested_error = NULL;
44 g_autoptr (GPtrArray) failures = NULL;
45
46 g_return_val_if_fail (yaml_file, NULL);
47 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
48
49 failures = g_ptr_array_new_with_free_func (g_object_unref);
50 idx = modulemd_module_index_new ();
51
52 ret = modulemd_module_index_update_from_file (
53 idx, yaml_file, FALSE, &failures, &nested_error);
54 return verify_load (ret, idx, failures, error, &nested_error);
55 }
56
57
58 ModulemdModuleIndex *
modulemd_load_string(const gchar * yaml_string,GError ** error)59 modulemd_load_string (const gchar *yaml_string, GError **error)
60 {
61 gboolean ret;
62 g_autoptr (ModulemdModuleIndex) idx = NULL;
63 g_autoptr (GError) nested_error = NULL;
64 g_autoptr (GPtrArray) failures = NULL;
65
66 g_return_val_if_fail (yaml_string, NULL);
67 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
68
69 failures = g_ptr_array_new_with_free_func (g_object_unref);
70 idx = modulemd_module_index_new ();
71
72 ret = modulemd_module_index_update_from_string (
73 idx, yaml_string, FALSE, &failures, &nested_error);
74 return verify_load (ret, idx, failures, error, &nested_error);
75 }
76
77
78 static ModulemdModuleIndex *
verify_load(gboolean ret,ModulemdModuleIndex * idx,GPtrArray * failures,GError ** error,GError ** nested_error)79 verify_load (gboolean ret,
80 ModulemdModuleIndex *idx,
81 GPtrArray *failures,
82 GError **error,
83 GError **nested_error)
84 {
85 if (!ret)
86 {
87 if (*nested_error)
88 {
89 g_propagate_error (error, g_steal_pointer (nested_error));
90 return NULL;
91 }
92 else if (failures && failures->len)
93 {
94 modulemd_subdocument_info_debug_dump_failures (failures);
95 g_set_error (error,
96 MODULEMD_ERROR,
97 MMD_ERROR_VALIDATE,
98 "One or more YAML subdocuments were invalid.");
99 return NULL;
100 }
101
102 /* Should never get here, must be a programming error */
103 g_set_error (
104 error, MODULEMD_ERROR, MMD_ERROR_VALIDATE, "Unknown internal error.");
105 g_return_val_if_reached (NULL);
106 }
107
108 return g_object_ref (idx);
109 }
110
111 static GType
112 modulemd_read_packager_from_parser (yaml_parser_t *parser,
113 GObject **object,
114 const gchar *module_name,
115 const gchar *module_stream,
116 GError **error);
117
118 GType
modulemd_read_packager_file(const gchar * yaml_path,GObject ** object,GError ** error)119 modulemd_read_packager_file (const gchar *yaml_path,
120 GObject **object,
121 GError **error)
122 {
123 return modulemd_read_packager_file_ext (
124 yaml_path, object, NULL, NULL, error);
125 }
126
127 GType
modulemd_read_packager_file_ext(const gchar * yaml_path,GObject ** object,const gchar * module_name,const gchar * module_stream,GError ** error)128 modulemd_read_packager_file_ext (const gchar *yaml_path,
129 GObject **object,
130 const gchar *module_name,
131 const gchar *module_stream,
132 GError **error)
133 {
134 MMD_INIT_YAML_PARSER (parser);
135 g_autoptr (FILE) yaml_stream = NULL;
136 gint err;
137
138 g_return_val_if_fail (yaml_path, G_TYPE_INVALID);
139 g_return_val_if_fail (object, G_TYPE_INVALID);
140 g_return_val_if_fail (error == NULL || *error == NULL, G_TYPE_INVALID);
141
142 errno = 0;
143 yaml_stream = g_fopen (yaml_path, "rbe");
144 err = errno;
145
146 if (!yaml_stream)
147 {
148 g_set_error (error,
149 MODULEMD_YAML_ERROR,
150 MMD_YAML_ERROR_OPEN,
151 "%s",
152 g_strerror (err));
153 return G_TYPE_INVALID;
154 }
155
156 yaml_parser_set_input_file (&parser, yaml_stream);
157
158 return modulemd_read_packager_from_parser (
159 &parser, object, module_name, module_stream, error);
160 }
161
162 GType
modulemd_read_packager_string(const gchar * yaml_string,GObject ** object,GError ** error)163 modulemd_read_packager_string (const gchar *yaml_string,
164 GObject **object,
165 GError **error)
166 {
167 return modulemd_read_packager_string_ext (
168 yaml_string, object, NULL, NULL, error);
169 }
170
171 GType
modulemd_read_packager_string_ext(const gchar * yaml_string,GObject ** object,const gchar * module_name,const gchar * module_stream,GError ** error)172 modulemd_read_packager_string_ext (const gchar *yaml_string,
173 GObject **object,
174 const gchar *module_name,
175 const gchar *module_stream,
176 GError **error)
177 {
178 MMD_INIT_YAML_PARSER (parser);
179
180 g_return_val_if_fail (yaml_string, G_TYPE_INVALID);
181 g_return_val_if_fail (object, G_TYPE_INVALID);
182 g_return_val_if_fail (error == NULL || *error == NULL, G_TYPE_INVALID);
183
184 yaml_parser_set_input_string (
185 &parser, (const unsigned char *)yaml_string, strlen (yaml_string));
186
187 return modulemd_read_packager_from_parser (
188 &parser, object, module_name, module_stream, error);
189 }
190
191 static GType
modulemd_read_packager_from_parser(yaml_parser_t * parser,GObject ** object,const gchar * module_name,const gchar * module_stream,GError ** error)192 modulemd_read_packager_from_parser (yaml_parser_t *parser,
193 GObject **object,
194 const gchar *module_name,
195 const gchar *module_stream,
196 GError **error)
197 {
198 MMD_INIT_YAML_EVENT (event);
199 g_autoptr (ModulemdModuleStreamV1) stream_v1 = NULL;
200 g_autoptr (ModulemdModuleStreamV2) stream_v2 = NULL;
201 g_autoptr (ModulemdPackagerV3) packager_v3 = NULL;
202 g_autoptr (ModulemdSubdocumentInfo) subdoc = NULL;
203 ModulemdYamlDocumentTypeEnum doctype;
204 const GError *gerror = NULL;
205 g_autoptr (GObject) return_object = NULL;
206 GType return_type = G_TYPE_INVALID;
207
208 /* The first event must be the stream start */
209 if (!yaml_parser_parse (parser, &event))
210 {
211 g_set_error_literal (error,
212 MODULEMD_YAML_ERROR,
213 MMD_YAML_ERROR_UNPARSEABLE,
214 "Parser error");
215 return G_TYPE_INVALID;
216 }
217 if (event.type != YAML_STREAM_START_EVENT)
218 {
219 g_set_error_literal (error,
220 MODULEMD_YAML_ERROR,
221 MMD_YAML_ERROR_PARSE,
222 "YAML didn't begin with STREAM_START.");
223 return G_TYPE_INVALID;
224 }
225 yaml_event_delete (&event);
226
227 /* The second event must be the document start */
228 if (!yaml_parser_parse (parser, &event))
229 {
230 g_set_error_literal (error,
231 MODULEMD_YAML_ERROR,
232 MMD_YAML_ERROR_UNPARSEABLE,
233 "Parser error");
234 return G_TYPE_INVALID;
235 }
236 if (event.type != YAML_DOCUMENT_START_EVENT)
237 {
238 g_set_error_literal (error,
239 MODULEMD_YAML_ERROR,
240 MMD_YAML_ERROR_PARSE,
241 "YAML didn't begin with STREAM_START.");
242 return G_TYPE_INVALID;
243 }
244 yaml_event_delete (&event);
245
246 subdoc = modulemd_yaml_parse_document_type (parser);
247 gerror = modulemd_subdocument_info_get_gerror (subdoc);
248 if (gerror)
249 {
250 g_set_error (error,
251 gerror->domain,
252 gerror->code,
253 "Parse error identifying document type and version: %s",
254 gerror->message);
255 return G_TYPE_INVALID;
256 }
257
258
259 doctype = modulemd_subdocument_info_get_doctype (subdoc);
260
261 switch (doctype)
262 {
263 case MODULEMD_YAML_DOC_PACKAGER:
264 if (modulemd_subdocument_info_get_mdversion (subdoc) <
265 MD_PACKAGER_VERSION_TWO)
266 {
267 g_set_error (error,
268 MODULEMD_YAML_ERROR,
269 MMD_YAML_ERROR_PARSE,
270 "Invalid mdversion for a packager document");
271 return G_TYPE_INVALID;
272 }
273
274 if (modulemd_subdocument_info_get_mdversion (subdoc) ==
275 MD_PACKAGER_VERSION_THREE)
276 {
277 packager_v3 = modulemd_packager_v3_parse_yaml (subdoc, error);
278 if (!packager_v3)
279 {
280 return G_TYPE_INVALID;
281 }
282
283 if (module_name)
284 {
285 modulemd_packager_v3_set_module_name (packager_v3, module_name);
286 }
287
288 if (module_stream)
289 {
290 modulemd_packager_v3_set_stream_name (packager_v3,
291 module_stream);
292 }
293
294 return_object = (GObject *)g_steal_pointer (&packager_v3);
295 return_type = MODULEMD_TYPE_PACKAGER_V3;
296 break;
297 }
298
299 /* Falling through intentionally: packager V2 format is handled below */
300
301 case MODULEMD_YAML_DOC_MODULESTREAM:
302 switch (modulemd_subdocument_info_get_mdversion (subdoc))
303 {
304 case MD_MODULESTREAM_VERSION_ONE:
305 stream_v1 =
306 modulemd_module_stream_v1_parse_yaml (subdoc, FALSE, error);
307 if (!stream_v1)
308 {
309 return G_TYPE_INVALID;
310 }
311
312 stream_v2 = MODULEMD_MODULE_STREAM_V2 (
313 modulemd_module_stream_upgrade_v1_to_v2 (
314 MODULEMD_MODULE_STREAM (stream_v1)));
315 if (!stream_v2)
316 {
317 /* This should be impossible, since there are no failure returns
318 * from modulemd_module_stream_upgrade_v1_to_v2()
319 */
320 g_set_error (error,
321 MODULEMD_ERROR,
322 MMD_ERROR_UPGRADE,
323 "Upgrading to v2 failed for an unknown reason");
324 return G_TYPE_INVALID;
325 }
326
327 if (module_name)
328 {
329 modulemd_module_stream_set_module_name (
330 MODULEMD_MODULE_STREAM (stream_v2), module_name);
331 }
332
333 if (module_stream)
334 {
335 modulemd_module_stream_set_stream_name (
336 MODULEMD_MODULE_STREAM (stream_v2), module_stream);
337 }
338
339 return_object = (GObject *)g_steal_pointer (&stream_v2);
340 return_type = MODULEMD_TYPE_MODULE_STREAM_V2;
341 break;
342
343 case MD_MODULESTREAM_VERSION_TWO:
344 stream_v2 = modulemd_module_stream_v2_parse_yaml (
345 subdoc, FALSE, doctype == MODULEMD_YAML_DOC_PACKAGER, error);
346 if (!stream_v2)
347 {
348 return G_TYPE_INVALID;
349 }
350
351 if (module_name)
352 {
353 modulemd_module_stream_set_module_name (
354 MODULEMD_MODULE_STREAM (stream_v2), module_name);
355 }
356
357 if (module_stream)
358 {
359 modulemd_module_stream_set_stream_name (
360 MODULEMD_MODULE_STREAM (stream_v2), module_stream);
361 }
362
363 return_object = (GObject *)g_steal_pointer (&stream_v2);
364 return_type = MODULEMD_TYPE_MODULE_STREAM_V2;
365 break;
366
367 default:
368 g_set_error (error,
369 MODULEMD_YAML_ERROR,
370 MMD_YAML_ERROR_PARSE,
371 "Invalid mdversion (%" PRIu64
372 ") for a modulemd[-stream] document",
373 modulemd_subdocument_info_get_mdversion (subdoc));
374 return G_TYPE_INVALID;
375 }
376 break;
377
378 default:
379 g_set_error (
380 error,
381 MODULEMD_YAML_ERROR,
382 MMD_YAML_ERROR_PARSE,
383 "Expected `document: modulemd[-stream] or modulemd-packager`, got %d",
384 modulemd_subdocument_info_get_doctype (subdoc));
385 return G_TYPE_INVALID;
386 }
387
388 /* The last event must be the stream end */
389 if (!yaml_parser_parse (parser, &event))
390 {
391 g_set_error_literal (error,
392 MODULEMD_YAML_ERROR,
393 MMD_YAML_ERROR_UNPARSEABLE,
394 "Parser error");
395 return G_TYPE_INVALID;
396 }
397
398 if (event.type != YAML_STREAM_END_EVENT)
399 {
400 g_set_error_literal (error,
401 MODULEMD_YAML_ERROR,
402 MMD_YAML_ERROR_PARSE,
403 "YAML contained more than a single subdocument");
404 return G_TYPE_INVALID;
405 }
406 yaml_event_delete (&event);
407
408 *object = g_steal_pointer (&return_object);
409 return return_type;
410 }
411