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 <glib.h>
15 #include <inttypes.h>
16 #include <yaml.h>
17 
18 #include "modulemd-errors.h"
19 #include "modulemd-obsoletes.h"
20 #include "private/modulemd-obsoletes-private.h"
21 #include "private/modulemd-util.h"
22 
23 #include "private/modulemd-subdocument-info-private.h"
24 #include "private/modulemd-util.h"
25 #include "private/modulemd-yaml.h"
26 
27 #define O_DEFAULT_STRING "__obsoletes_VALUE_UNSET__"
28 #define O_PLACEHOLDER_STRING "__obsoletes_VALUE_NOT_YET_SET__"
29 
30 struct _ModulemdObsoletes
31 {
32   GObject parent_instance;
33 
34   guint64 mdversion;
35   guint64 modified;
36   gboolean reset;
37   gchar *module_name;
38   gchar *module_stream;
39   gchar *module_context;
40   guint64 eol_date;
41   gchar *message;
42 
43   /* Stream is obsoleted by exactly one other stream */
44   gchar *obsoleted_by_module_name;
45   gchar *obsoleted_by_module_stream;
46 };
47 
48 G_DEFINE_TYPE (ModulemdObsoletes, modulemd_obsoletes, G_TYPE_OBJECT)
49 
50 enum
51 {
52   PROP_0,
53 
54   PROP_MDVERSION,
55   PROP_MODIFIED,
56   PROP_RESET,
57   PROP_MODULE_NAME,
58   PROP_MODULE_STREAM,
59   PROP_MODULE_CONTEXT,
60   PROP_EOL_DATE,
61   PROP_MESSAGE,
62   PROP_OBSOLETED_BY_MODULE_NAME,
63   PROP_OBSOLETED_BY_MODULE_STREAM,
64 
65   N_PROPS
66 };
67 
68 static GParamSpec *properties[N_PROPS];
69 
70 ModulemdObsoletes *
modulemd_obsoletes_new(guint64 mdversion,guint64 modified,const gchar * module_name,const gchar * module_stream,const gchar * message)71 modulemd_obsoletes_new (guint64 mdversion,
72                         guint64 modified,
73                         const gchar *module_name,
74                         const gchar *module_stream,
75                         const gchar *message)
76 {
77   // clang-format off
78   return g_object_new (MODULEMD_TYPE_OBSOLETES,
79                        "mdversion", mdversion,
80                        "modified", modified,
81                        "module-name", module_name,
82                        "module-stream", module_stream,
83                        "message", message,
84                        NULL);
85   // clang-format on
86 }
87 
88 
89 ModulemdObsoletes *
modulemd_obsoletes_copy(ModulemdObsoletes * self)90 modulemd_obsoletes_copy (ModulemdObsoletes *self)
91 {
92   g_autoptr (ModulemdObsoletes) o = NULL;
93 
94   g_return_val_if_fail (MODULEMD_IS_OBSOLETES (self), NULL);
95 
96 
97   o = modulemd_obsoletes_new (modulemd_obsoletes_get_mdversion (self),
98                               modulemd_obsoletes_get_modified (self),
99                               modulemd_obsoletes_get_module_name (self),
100                               modulemd_obsoletes_get_module_stream (self),
101                               modulemd_obsoletes_get_message (self));
102   modulemd_obsoletes_set_module_context (
103     o, modulemd_obsoletes_get_module_context (self));
104   modulemd_obsoletes_set_reset (o, modulemd_obsoletes_get_reset (self));
105   modulemd_obsoletes_set_eol_date (o, modulemd_obsoletes_get_eol_date (self));
106   modulemd_obsoletes_set_obsoleted_by_module_name (
107     o, modulemd_obsoletes_get_obsoleted_by_module_name (self));
108   modulemd_obsoletes_set_obsoleted_by_module_stream (
109     o, modulemd_obsoletes_get_obsoleted_by_module_stream (self));
110 
111   return g_steal_pointer (&o);
112 }
113 
114 gboolean
modulemd_obsoletes_validate(ModulemdObsoletes * self,GError ** error)115 modulemd_obsoletes_validate (ModulemdObsoletes *self, GError **error)
116 {
117   g_return_val_if_fail (MODULEMD_IS_OBSOLETES (self), FALSE);
118   guint64 mdversion = modulemd_obsoletes_get_mdversion (self);
119 
120   if (!mdversion)
121     {
122       g_set_error_literal (error,
123                            MODULEMD_ERROR,
124                            MMD_ERROR_VALIDATE,
125                            "Metadata version is unset.");
126       return FALSE;
127     }
128   if (mdversion > MD_OBSOLETES_VERSION_LATEST)
129     {
130       g_set_error (error,
131                    MODULEMD_ERROR,
132                    MMD_ERROR_VALIDATE,
133                    "Metadata version unknown: %" PRIu64 ".",
134                    mdversion);
135       return FALSE;
136     }
137 
138   if (modulemd_obsoletes_get_modified (self) == 0)
139     {
140       g_set_error_literal (error,
141                            MODULEMD_ERROR,
142                            MMD_ERROR_VALIDATE,
143                            "Obsoletes modified is empty.");
144       return FALSE;
145     }
146 
147   if (!g_strcmp0 (modulemd_obsoletes_get_module_name (self),
148                   O_PLACEHOLDER_STRING) ||
149       modulemd_obsoletes_get_module_name (self)[0] == '\0')
150     {
151       g_set_error_literal (error,
152                            MODULEMD_ERROR,
153                            MMD_ERROR_VALIDATE,
154                            "Obsoletes module name is unset.");
155       return FALSE;
156     }
157 
158   if (!g_strcmp0 (modulemd_obsoletes_get_module_stream (self),
159                   O_PLACEHOLDER_STRING) ||
160       modulemd_obsoletes_get_module_stream (self)[0] == '\0')
161     {
162       g_set_error_literal (error,
163                            MODULEMD_ERROR,
164                            MMD_ERROR_VALIDATE,
165                            "Obsoletes stream is unset.");
166       return FALSE;
167     }
168 
169   if (!g_strcmp0 (modulemd_obsoletes_get_message (self),
170                   O_PLACEHOLDER_STRING) ||
171       !g_strcmp0 (modulemd_obsoletes_get_message (self), O_DEFAULT_STRING) ||
172       modulemd_obsoletes_get_message (self)[0] == '\0')
173     {
174       g_set_error_literal (error,
175                            MODULEMD_ERROR,
176                            MMD_ERROR_VALIDATE,
177                            "Obsoletes message is unset.");
178       return FALSE;
179     }
180 
181   /* It should not be possible to set reset and eol_date
182    * at the same time */
183   if (modulemd_obsoletes_get_reset (self) &&
184       modulemd_obsoletes_get_eol_date (self))
185     {
186       g_set_error_literal (
187         error,
188         MODULEMD_ERROR,
189         MMD_ERROR_VALIDATE,
190         "Obsoletes cannot have both eol_date and reset attributes set.");
191       return FALSE;
192     }
193   /* It should not be possible to set reset and obsoleted_by*
194    * at the same time */
195   if (modulemd_obsoletes_get_reset (self) &&
196       (modulemd_obsoletes_get_obsoleted_by_module_name (self) ||
197        modulemd_obsoletes_get_obsoleted_by_module_stream (self)))
198     {
199       g_set_error_literal (
200         error,
201         MODULEMD_ERROR,
202         MMD_ERROR_VALIDATE,
203         "Obsoletes cannot have both obsoleted_by and reset attributes set.");
204       return FALSE;
205     }
206 
207   if ((modulemd_obsoletes_get_obsoleted_by_module_name (self) &&
208        !modulemd_obsoletes_get_obsoleted_by_module_stream (self)) ||
209       (!modulemd_obsoletes_get_obsoleted_by_module_name (self) &&
210        modulemd_obsoletes_get_obsoleted_by_module_stream (self)))
211     {
212       g_set_error_literal (
213         error,
214         MODULEMD_ERROR,
215         MMD_ERROR_VALIDATE,
216         "Obsoletes obsoleted by module name and module stream "
217         "have to be set together.");
218       return FALSE;
219     }
220 
221   return TRUE;
222 }
223 
224 static void
modulemd_obsoletes_finalize(GObject * object)225 modulemd_obsoletes_finalize (GObject *object)
226 {
227   ModulemdObsoletes *self = (ModulemdObsoletes *)object;
228 
229   g_clear_pointer (&self->module_name, g_free);
230   g_clear_pointer (&self->module_stream, g_free);
231   g_clear_pointer (&self->module_context, g_free);
232   g_clear_pointer (&self->message, g_free);
233   g_clear_pointer (&self->obsoleted_by_module_name, g_free);
234   g_clear_pointer (&self->obsoleted_by_module_stream, g_free);
235 
236   G_OBJECT_CLASS (modulemd_obsoletes_parent_class)->finalize (object);
237 }
238 
239 guint64
modulemd_obsoletes_get_mdversion(ModulemdObsoletes * self)240 modulemd_obsoletes_get_mdversion (ModulemdObsoletes *self)
241 {
242   return self->mdversion;
243 }
244 
245 
246 guint64
modulemd_obsoletes_get_modified(ModulemdObsoletes * self)247 modulemd_obsoletes_get_modified (ModulemdObsoletes *self)
248 {
249   return self->modified;
250 }
251 
252 
253 gboolean
modulemd_obsoletes_get_reset(ModulemdObsoletes * self)254 modulemd_obsoletes_get_reset (ModulemdObsoletes *self)
255 {
256   return self->reset;
257 }
258 
259 
260 const gchar *
modulemd_obsoletes_get_module_name(ModulemdObsoletes * self)261 modulemd_obsoletes_get_module_name (ModulemdObsoletes *self)
262 {
263   return self->module_name;
264 }
265 
266 
267 const gchar *
modulemd_obsoletes_get_module_stream(ModulemdObsoletes * self)268 modulemd_obsoletes_get_module_stream (ModulemdObsoletes *self)
269 {
270   return self->module_stream;
271 }
272 
273 
274 const gchar *
modulemd_obsoletes_get_module_context(ModulemdObsoletes * self)275 modulemd_obsoletes_get_module_context (ModulemdObsoletes *self)
276 {
277   return self->module_context;
278 }
279 
280 
281 guint64
modulemd_obsoletes_get_eol_date(ModulemdObsoletes * self)282 modulemd_obsoletes_get_eol_date (ModulemdObsoletes *self)
283 {
284   return self->eol_date;
285 }
286 
287 
288 const gchar *
modulemd_obsoletes_get_message(ModulemdObsoletes * self)289 modulemd_obsoletes_get_message (ModulemdObsoletes *self)
290 {
291   return self->message;
292 }
293 
294 
295 const gchar *
modulemd_obsoletes_get_obsoleted_by_module_name(ModulemdObsoletes * self)296 modulemd_obsoletes_get_obsoleted_by_module_name (ModulemdObsoletes *self)
297 {
298   return self->obsoleted_by_module_name;
299 }
300 
301 
302 const gchar *
modulemd_obsoletes_get_obsoleted_by_module_stream(ModulemdObsoletes * self)303 modulemd_obsoletes_get_obsoleted_by_module_stream (ModulemdObsoletes *self)
304 {
305   return self->obsoleted_by_module_stream;
306 }
307 
308 
309 static void
modulemd_obsoletes_set_mdversion(ModulemdObsoletes * self,guint64 mdversion)310 modulemd_obsoletes_set_mdversion (ModulemdObsoletes *self, guint64 mdversion)
311 {
312   g_return_if_fail (MODULEMD_IS_OBSOLETES (self));
313   g_return_if_fail (mdversion != 0);
314 
315   self->mdversion = mdversion;
316 
317   g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MDVERSION]);
318 }
319 
320 void
modulemd_obsoletes_set_modified(ModulemdObsoletes * self,guint64 modified)321 modulemd_obsoletes_set_modified (ModulemdObsoletes *self, guint64 modified)
322 {
323   g_return_if_fail (MODULEMD_IS_OBSOLETES (self));
324 
325   self->modified = modified;
326 
327   g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MODIFIED]);
328 }
329 
330 
331 void
modulemd_obsoletes_set_reset(ModulemdObsoletes * self,gboolean reset)332 modulemd_obsoletes_set_reset (ModulemdObsoletes *self, gboolean reset)
333 {
334   g_return_if_fail (MODULEMD_IS_OBSOLETES (self));
335 
336   self->reset = reset;
337 
338   g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_RESET]);
339 }
340 
341 
342 static void
modulemd_obsoletes_set_module_name(ModulemdObsoletes * self,const gchar * module_name)343 modulemd_obsoletes_set_module_name (ModulemdObsoletes *self,
344                                     const gchar *module_name)
345 {
346   g_return_if_fail (MODULEMD_IS_OBSOLETES (self));
347   g_return_if_fail (module_name);
348   g_return_if_fail (g_strcmp0 (module_name, O_DEFAULT_STRING));
349 
350   g_clear_pointer (&self->module_name, g_free);
351   self->module_name = g_strdup (module_name);
352 
353   g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MODULE_NAME]);
354 }
355 
356 
357 static void
modulemd_obsoletes_set_module_stream(ModulemdObsoletes * self,const gchar * module_stream)358 modulemd_obsoletes_set_module_stream (ModulemdObsoletes *self,
359                                       const gchar *module_stream)
360 {
361   g_return_if_fail (MODULEMD_IS_OBSOLETES (self));
362   g_return_if_fail (module_stream);
363   g_return_if_fail (g_strcmp0 (module_stream, O_DEFAULT_STRING));
364 
365   g_clear_pointer (&self->module_stream, g_free);
366   self->module_stream = g_strdup (module_stream);
367 
368   g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MODULE_STREAM]);
369 }
370 
371 
372 void
modulemd_obsoletes_set_module_context(ModulemdObsoletes * self,const gchar * module_context)373 modulemd_obsoletes_set_module_context (ModulemdObsoletes *self,
374                                        const gchar *module_context)
375 {
376   g_return_if_fail (MODULEMD_IS_OBSOLETES (self));
377 
378   g_clear_pointer (&self->module_context, g_free);
379   self->module_context = g_strdup (module_context);
380 
381   g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MODULE_CONTEXT]);
382 }
383 
384 void
modulemd_obsoletes_set_eol_date(ModulemdObsoletes * self,guint64 eol_date)385 modulemd_obsoletes_set_eol_date (ModulemdObsoletes *self, guint64 eol_date)
386 {
387   g_return_if_fail (MODULEMD_IS_OBSOLETES (self));
388 
389   self->eol_date = eol_date;
390 
391   g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_EOL_DATE]);
392 }
393 
394 
395 void
modulemd_obsoletes_set_message(ModulemdObsoletes * self,const gchar * message)396 modulemd_obsoletes_set_message (ModulemdObsoletes *self, const gchar *message)
397 {
398   g_return_if_fail (MODULEMD_IS_OBSOLETES (self));
399   g_return_if_fail (message);
400 
401   g_clear_pointer (&self->message, g_free);
402   self->message = g_strdup (message);
403 
404   g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MESSAGE]);
405 }
406 
407 void
modulemd_obsoletes_set_obsoleted_by_module_name(ModulemdObsoletes * self,const gchar * obsoleted_by_module_name)408 modulemd_obsoletes_set_obsoleted_by_module_name (
409   ModulemdObsoletes *self, const gchar *obsoleted_by_module_name)
410 {
411   g_return_if_fail (MODULEMD_IS_OBSOLETES (self));
412 
413   g_clear_pointer (&self->obsoleted_by_module_name, g_free);
414   self->obsoleted_by_module_name = g_strdup (obsoleted_by_module_name);
415 
416   g_object_notify_by_pspec (G_OBJECT (self),
417                             properties[PROP_OBSOLETED_BY_MODULE_NAME]);
418 }
419 
420 void
modulemd_obsoletes_set_obsoleted_by_module_stream(ModulemdObsoletes * self,const gchar * obsoleted_by_module_stream)421 modulemd_obsoletes_set_obsoleted_by_module_stream (
422   ModulemdObsoletes *self, const gchar *obsoleted_by_module_stream)
423 {
424   g_return_if_fail (MODULEMD_IS_OBSOLETES (self));
425 
426   g_clear_pointer (&self->obsoleted_by_module_stream, g_free);
427   self->obsoleted_by_module_stream = g_strdup (obsoleted_by_module_stream);
428 
429   g_object_notify_by_pspec (G_OBJECT (self),
430                             properties[PROP_OBSOLETED_BY_MODULE_STREAM]);
431 }
432 
433 void
modulemd_obsoletes_set_obsoleted_by(ModulemdObsoletes * self,const gchar * obsoleted_by_module_name,const gchar * obsoleted_by_module_stream)434 modulemd_obsoletes_set_obsoleted_by (ModulemdObsoletes *self,
435                                      const gchar *obsoleted_by_module_name,
436                                      const gchar *obsoleted_by_module_stream)
437 {
438   g_return_if_fail (MODULEMD_IS_OBSOLETES (self));
439 
440   modulemd_obsoletes_set_obsoleted_by_module_name (self,
441                                                    obsoleted_by_module_name);
442   modulemd_obsoletes_set_obsoleted_by_module_stream (
443     self, obsoleted_by_module_stream);
444 }
445 
446 
447 static void
modulemd_obsoletes_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)448 modulemd_obsoletes_get_property (GObject *object,
449                                  guint prop_id,
450                                  GValue *value,
451                                  GParamSpec *pspec)
452 {
453   ModulemdObsoletes *self = MODULEMD_OBSOLETES (object);
454 
455   switch (prop_id)
456     {
457     case PROP_MDVERSION:
458       g_value_set_uint64 (value, modulemd_obsoletes_get_mdversion (self));
459       break;
460     case PROP_MODIFIED:
461       g_value_set_uint64 (value, modulemd_obsoletes_get_modified (self));
462       break;
463     case PROP_RESET:
464       g_value_set_boolean (value, modulemd_obsoletes_get_reset (self));
465       break;
466     case PROP_MODULE_NAME:
467       g_value_set_string (value, modulemd_obsoletes_get_module_name (self));
468       break;
469     case PROP_MODULE_STREAM:
470       g_value_set_string (value, modulemd_obsoletes_get_module_stream (self));
471       break;
472     case PROP_MODULE_CONTEXT:
473       g_value_set_string (value, modulemd_obsoletes_get_module_context (self));
474       break;
475     case PROP_EOL_DATE:
476       g_value_set_uint64 (value, modulemd_obsoletes_get_eol_date (self));
477       break;
478     case PROP_MESSAGE:
479       g_value_set_string (value, modulemd_obsoletes_get_message (self));
480       break;
481     case PROP_OBSOLETED_BY_MODULE_NAME:
482       g_value_set_string (
483         value, modulemd_obsoletes_get_obsoleted_by_module_name (self));
484       break;
485     case PROP_OBSOLETED_BY_MODULE_STREAM:
486       g_value_set_string (
487         value, modulemd_obsoletes_get_obsoleted_by_module_stream (self));
488       break;
489     default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
490     }
491 }
492 
493 
494 static void
modulemd_obsoletes_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)495 modulemd_obsoletes_set_property (GObject *object,
496                                  guint prop_id,
497                                  const GValue *value,
498                                  GParamSpec *pspec)
499 {
500   ModulemdObsoletes *self = MODULEMD_OBSOLETES (object);
501 
502   switch (prop_id)
503     {
504     case PROP_MDVERSION:
505       modulemd_obsoletes_set_mdversion (self, g_value_get_uint64 (value));
506       break;
507     case PROP_MODIFIED:
508       modulemd_obsoletes_set_modified (self, g_value_get_uint64 (value));
509       break;
510     case PROP_RESET:
511       modulemd_obsoletes_set_reset (self, g_value_get_boolean (value));
512       break;
513     case PROP_MODULE_NAME:
514       modulemd_obsoletes_set_module_name (self, g_value_get_string (value));
515       break;
516     case PROP_MODULE_STREAM:
517       modulemd_obsoletes_set_module_stream (self, g_value_get_string (value));
518       break;
519     case PROP_MODULE_CONTEXT:
520       modulemd_obsoletes_set_module_context (self, g_value_get_string (value));
521       break;
522     case PROP_EOL_DATE:
523       modulemd_obsoletes_set_eol_date (self, g_value_get_uint64 (value));
524       break;
525     case PROP_MESSAGE:
526       modulemd_obsoletes_set_message (self, g_value_get_string (value));
527       break;
528     case PROP_OBSOLETED_BY_MODULE_NAME:
529       modulemd_obsoletes_set_obsoleted_by_module_name (
530         self, g_value_get_string (value));
531       break;
532     case PROP_OBSOLETED_BY_MODULE_STREAM:
533       modulemd_obsoletes_set_obsoleted_by_module_stream (
534         self, g_value_get_string (value));
535       break;
536     default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
537     }
538 }
539 
540 static void
modulemd_obsoletes_class_init(ModulemdObsoletesClass * klass)541 modulemd_obsoletes_class_init (ModulemdObsoletesClass *klass)
542 {
543   GObjectClass *object_class = G_OBJECT_CLASS (klass);
544 
545   object_class->finalize = modulemd_obsoletes_finalize;
546   object_class->get_property = modulemd_obsoletes_get_property;
547   object_class->set_property = modulemd_obsoletes_set_property;
548 
549   properties[PROP_MDVERSION] = g_param_spec_uint64 (
550     "mdversion",
551     "Metadata Version",
552     "The metadata version of this obsoletes object.",
553     0,
554     G_MAXUINT64,
555     0,
556     G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY);
557 
558   properties[PROP_MODIFIED] = g_param_spec_uint64 (
559     "modified",
560     "Modified",
561     "The last modified time represented as a 64-bit integer "
562     "(such as 201807011200)",
563     0,
564     G_MAXUINT64,
565     0,
566     G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT);
567 
568   properties[PROP_MODULE_NAME] = g_param_spec_string (
569     "module-name",
570     "Module name",
571     "The name of the module to which this obsoletes applies.",
572     O_DEFAULT_STRING,
573     G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY);
574 
575   properties[PROP_RESET] = g_param_spec_boolean (
576     "override-previous",
577     "Override previous",
578     "A boolean option to cancel/reset all previously specified obsoletes.",
579     FALSE,
580     G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT);
581 
582   properties[PROP_MODULE_STREAM] = g_param_spec_string (
583     "module-stream",
584     "Module stream",
585     "The name of the module stream to which this obsoletes applies.",
586     O_DEFAULT_STRING,
587     G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY);
588 
589   properties[PROP_MODULE_CONTEXT] = g_param_spec_string (
590     "module-context",
591     "Module context",
592     "The name of the module context to which this obsoletes applies.",
593     NULL,
594     G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT);
595 
596   properties[PROP_EOL_DATE] = g_param_spec_uint64 (
597     "eol-date",
598     "EOL date",
599     "A string representing UTC date in ISO 8601 format: YYYY-MM-DDTHH:MMZ",
600     0,
601     G_MAXUINT64,
602     0,
603     G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT);
604 
605   properties[PROP_MESSAGE] = g_param_spec_string (
606     "message",
607     "Message",
608     "A string describing the change, reason, etc.",
609     O_DEFAULT_STRING,
610     G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY);
611 
612   properties[PROP_OBSOLETED_BY_MODULE_NAME] = g_param_spec_string (
613     "obsoleted-by-module-name",
614     "Obsoleted by module name",
615     "Name of the module that obsoletes this one.",
616     NULL,
617     G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT);
618 
619   properties[PROP_OBSOLETED_BY_MODULE_STREAM] = g_param_spec_string (
620     "obsoleted-by-module-stream",
621     "Obsoleted by module stream",
622     "Stream of the module that obsoletes this one.",
623     NULL,
624     G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT);
625 
626   g_object_class_install_properties (object_class, N_PROPS, properties);
627 }
628 
629 static void
modulemd_obsoletes_init(ModulemdObsoletes * self)630 modulemd_obsoletes_init (ModulemdObsoletes *self)
631 {
632 }
633 
634 /* === YAML Functions === */
635 static gboolean
modulemd_obsoletes_parse_obsoleted_by(yaml_parser_t * parser,ModulemdObsoletes * o,gboolean strict,GError ** error)636 modulemd_obsoletes_parse_obsoleted_by (yaml_parser_t *parser,
637                                        ModulemdObsoletes *o,
638                                        gboolean strict,
639                                        GError **error)
640 {
641   MODULEMD_INIT_TRACE ();
642   MMD_INIT_YAML_EVENT (event);
643   g_autoptr (GError) nested_error = NULL;
644   gchar *value = NULL;
645   gboolean done = FALSE;
646 
647   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
648   YAML_PARSER_PARSE_WITH_EXIT_BOOL (parser, &event, error);
649   if (event.type != YAML_MAPPING_START_EVENT)
650     {
651       MMD_YAML_ERROR_EVENT_EXIT_BOOL (
652         error, event, "Missing mapping in obsoletes obsoleted_by.");
653     }
654 
655   while (!done)
656     {
657       YAML_PARSER_PARSE_WITH_EXIT_BOOL (parser, &event, error);
658       switch (event.type)
659         {
660         case YAML_MAPPING_END_EVENT: done = TRUE; break;
661 
662         case YAML_SCALAR_EVENT:
663           if (g_str_equal (event.data.scalar.value, "module"))
664             {
665               if (modulemd_obsoletes_get_obsoleted_by_module_name (o))
666                 {
667                   /* We already have a module name. It should not appear
668                    * twice in the same document.
669                    */
670                   MMD_YAML_ERROR_EVENT_EXIT_BOOL (
671                     error,
672                     event,
673                     "Obsoleted by module name encountered twice.");
674                 }
675 
676               value = modulemd_yaml_parse_string (parser, &nested_error);
677               if (!value)
678                 {
679                   MMD_YAML_ERROR_EVENT_EXIT_BOOL (
680                     error,
681                     event,
682                     "Failed to parse module name in obsoletes obsoleted_by "
683                     "data: %s",
684                     nested_error->message);
685                 }
686               modulemd_obsoletes_set_obsoleted_by_module_name (o, value);
687               g_clear_pointer (&value, g_free);
688             }
689 
690           if (g_str_equal (event.data.scalar.value, "stream"))
691             {
692               if (modulemd_obsoletes_get_obsoleted_by_module_stream (o))
693                 {
694                   /* We already have a module stream. It should not appear
695                    * twice in the same document.
696                    */
697                   MMD_YAML_ERROR_EVENT_EXIT_BOOL (
698                     error,
699                     event,
700                     "Obsoleted by module stream encountered twice.");
701                 }
702 
703               value = modulemd_yaml_parse_string (parser, &nested_error);
704               if (!value)
705                 {
706                   MMD_YAML_ERROR_EVENT_EXIT_BOOL (
707                     error,
708                     event,
709                     "Failed to parse module stream in obsoletes obsoleted_by "
710                     "data: %s",
711                     nested_error->message);
712                 }
713               modulemd_obsoletes_set_obsoleted_by_module_stream (o, value);
714               g_clear_pointer (&value, g_free);
715             }
716           break;
717 
718         default:
719           MMD_YAML_ERROR_EVENT_EXIT_BOOL (
720             error, event, "Unexpected YAML event in obsoletes obsoleted_by.");
721           break;
722         }
723       yaml_event_delete (&event);
724     }
725 
726   return TRUE;
727 }
728 
729 ModulemdObsoletes *
modulemd_obsoletes_parse_yaml(ModulemdSubdocumentInfo * subdoc,gboolean strict,GError ** error)730 modulemd_obsoletes_parse_yaml (ModulemdSubdocumentInfo *subdoc,
731                                gboolean strict,
732                                GError **error)
733 {
734   MODULEMD_INIT_TRACE ();
735   MMD_INIT_YAML_PARSER (parser);
736   MMD_INIT_YAML_EVENT (event);
737   g_autoptr (GError) nested_error = NULL;
738   gboolean done = FALSE;
739 
740   g_autofree gchar *value = NULL;
741   g_autofree gchar *obs_by_mod_name = NULL;
742   g_autofree gchar *obs_by_mod_stream = NULL;
743   guint64 modified;
744   guint64 eol_date;
745   gboolean reset;
746 
747   g_autoptr (ModulemdObsoletes) o = NULL;
748 
749   guint64 mdversion = modulemd_subdocument_info_get_mdversion (subdoc);
750 
751   if (!modulemd_subdocument_info_get_data_parser (
752         subdoc, &parser, strict, error))
753     {
754       g_debug ("get_data_parser() failed: %s", (*error)->message);
755       return NULL;
756     }
757 
758   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
759 
760   /* Create an obsoletes with placeholder values. We'll verify that this has been
761    * changed before we return it. This is because we can't guarantee that we
762    * will get the actual values from the YAML before reading any of the other
763    * data, but it's easier to process the rest of the contents with the
764    * constructed object.
765    */
766   o = modulemd_obsoletes_new (mdversion,
767                               0,
768                               O_PLACEHOLDER_STRING,
769                               O_PLACEHOLDER_STRING,
770                               O_PLACEHOLDER_STRING);
771 
772   YAML_PARSER_PARSE_WITH_EXIT (&parser, &event, error);
773   if (event.type != YAML_MAPPING_START_EVENT)
774     {
775       MMD_YAML_ERROR_EVENT_EXIT (
776         error, event, "Missing START EVENT mapping in obsoletes data entry");
777     }
778 
779   //parsing loop
780   while (!done)
781     {
782       YAML_PARSER_PARSE_WITH_EXIT (&parser, &event, error);
783 
784       switch (event.type)
785         {
786         case YAML_MAPPING_END_EVENT: done = TRUE; break;
787 
788         case YAML_SCALAR_EVENT:
789           if (g_str_equal (event.data.scalar.value, "module"))
790             {
791               if (!g_str_equal (modulemd_obsoletes_get_module_name (o),
792                                 O_PLACEHOLDER_STRING))
793                 {
794                   /* The module name was set earlier, which means it is
795                    * not expected here
796                    */
797                   MMD_YAML_ERROR_EVENT_EXIT (
798                     error, event, "Module name encountered twice");
799                 }
800               value = modulemd_yaml_parse_string (&parser, &nested_error);
801               if (!value)
802                 {
803                   MMD_YAML_ERROR_EVENT_EXIT (
804                     error,
805                     event,
806                     "Failed to parse module name in obsoletes data: %s",
807                     nested_error->message);
808                 }
809 
810               /* Use a private internal function to set the module_name.
811                * External consumers should never be allowed to change this
812                * value, but we need to be able to modify the placeholder.
813                */
814               modulemd_obsoletes_set_module_name (o, value);
815               g_clear_pointer (&value, g_free);
816             }
817           else if (g_str_equal (event.data.scalar.value, "stream"))
818             {
819               if (!g_str_equal (modulemd_obsoletes_get_module_stream (o),
820                                 O_PLACEHOLDER_STRING))
821                 {
822                   MMD_YAML_ERROR_EVENT_EXIT (
823                     error, event, "Module stream encountered twice");
824                 }
825               value = modulemd_yaml_parse_string (&parser, &nested_error);
826               if (!value)
827                 {
828                   MMD_YAML_ERROR_EVENT_EXIT (
829                     error,
830                     event,
831                     "Failed to parse module stream in obsoletes data: %s",
832                     nested_error->message);
833                 }
834 
835               modulemd_obsoletes_set_module_stream (o, value);
836               g_clear_pointer (&value, g_free);
837             }
838           else if (g_str_equal (event.data.scalar.value, "context"))
839             {
840               if (modulemd_obsoletes_get_module_context (o))
841                 {
842                   MMD_YAML_ERROR_EVENT_EXIT (
843                     error, event, "Module context encountered twice");
844                 }
845               value = modulemd_yaml_parse_string (&parser, &nested_error);
846               if (!value)
847                 {
848                   MMD_YAML_ERROR_EVENT_EXIT (
849                     error,
850                     event,
851                     "Failed to parse module context in obsoletes data: %s",
852                     nested_error->message);
853                 }
854 
855               modulemd_obsoletes_set_module_context (o, value);
856               g_clear_pointer (&value, g_free);
857             }
858           else if (g_str_equal (event.data.scalar.value, "modified"))
859             {
860               value = modulemd_yaml_parse_string (&parser, &nested_error);
861               if (nested_error)
862                 {
863                   MMD_YAML_ERROR_EVENT_EXIT (
864                     error,
865                     event,
866                     "Failed to parse modified in obsoletes data: %s",
867                     nested_error->message);
868                 }
869               modified = modulemd_iso8601date_to_guint64 (value);
870               if (modified == 0)
871                 {
872                   MMD_YAML_ERROR_EVENT_EXIT (
873                     error,
874                     event,
875                     "Failed to parse UTC date in ISO 8601 format: "
876                     "YYYY-MM-DDTHH:MMZ modified in eol data: %s",
877                     value);
878                 }
879 
880               modulemd_obsoletes_set_modified (o, modified);
881               g_clear_pointer (&value, g_free);
882             }
883           else if (g_str_equal (event.data.scalar.value, "eol_date"))
884             {
885               value = modulemd_yaml_parse_string (&parser, &nested_error);
886               if (nested_error)
887                 {
888                   MMD_YAML_ERROR_EVENT_EXIT (
889                     error,
890                     event,
891                     "Failed to parse eol_date in obsoletes data: %s",
892                     nested_error->message);
893                 }
894 
895               eol_date = modulemd_iso8601date_to_guint64 (value);
896               if (eol_date == 0)
897                 {
898                   MMD_YAML_ERROR_EVENT_EXIT (
899                     error,
900                     event,
901                     "Failed to parse UTC date in ISO 8601 format: "
902                     "YYYY-MM-DD[T ]HH:MMZ eol_date in obsoletes data: %s",
903                     value);
904                 }
905 
906               modulemd_obsoletes_set_eol_date (o, eol_date);
907               g_clear_pointer (&value, g_free);
908             }
909           else if (g_str_equal (event.data.scalar.value, "reset"))
910             {
911               reset = modulemd_yaml_parse_bool (&parser, &nested_error);
912               if (nested_error)
913                 {
914                   MMD_YAML_ERROR_EVENT_EXIT (
915                     error,
916                     event,
917                     "Failed to parse reset in obsoletes data: %s",
918                     nested_error->message);
919                 }
920 
921               modulemd_obsoletes_set_reset (o, reset);
922             }
923           else if (g_str_equal (event.data.scalar.value, "message"))
924             {
925               if (!g_str_equal (modulemd_obsoletes_get_message (o),
926                                 O_PLACEHOLDER_STRING))
927                 {
928                   MMD_YAML_ERROR_EVENT_EXIT (
929                     error, event, "Module message encountered twice");
930                 }
931               value = modulemd_yaml_parse_string (&parser, &nested_error);
932               if (!value)
933                 {
934                   MMD_YAML_ERROR_EVENT_EXIT (
935                     error,
936                     event,
937                     "Failed to parse message in obsoletes data: %s",
938                     nested_error->message);
939                 }
940 
941               modulemd_obsoletes_set_message (o, value);
942               g_clear_pointer (&value, g_free);
943             }
944           else if (g_str_equal (event.data.scalar.value, "obsoleted_by"))
945             {
946               if (!modulemd_obsoletes_parse_obsoleted_by (
947                     &parser, o, strict, &nested_error))
948                 {
949                   g_propagate_error (error, g_steal_pointer (&nested_error));
950                   return NULL;
951                 }
952             }
953           else
954             {
955               SKIP_UNKNOWN (&parser,
956                             NULL,
957                             "Unexpected key in obsoletes data: %s",
958                             (const gchar *)event.data.scalar.value);
959               break;
960             }
961           break;
962 
963         default:
964           MMD_YAML_ERROR_EVENT_EXIT (
965             error,
966             event,
967             "Unexpected YAML event %s in obsoletes data",
968             mmd_yaml_get_event_name (event.type));
969           break;
970         }
971 
972       yaml_event_delete (&event);
973     }
974 
975   if (!modulemd_obsoletes_validate (o, &nested_error))
976     {
977       g_propagate_error (error, g_steal_pointer (&nested_error));
978       return NULL;
979     }
980 
981   return g_steal_pointer (&o);
982 }
983 
984 
985 static gboolean
modulemd_obsoletes_emit_obsoleted_by(ModulemdObsoletes * self,yaml_emitter_t * emitter,GError ** error)986 modulemd_obsoletes_emit_obsoleted_by (ModulemdObsoletes *self,
987                                       yaml_emitter_t *emitter,
988                                       GError **error)
989 {
990   /* Start the "obsoleted_by:" section */
991   if (!mmd_emitter_scalar (
992         emitter, "obsoleted_by", YAML_PLAIN_SCALAR_STYLE, error))
993     {
994       return FALSE;
995     }
996 
997   /* Start the mapping for "obsoleted_by:" */
998   if (!mmd_emitter_start_mapping (emitter, YAML_BLOCK_MAPPING_STYLE, error))
999     {
1000       return FALSE;
1001     }
1002 
1003   /* The module name is mandatory if already in obsoleted_by */
1004   if (!mmd_emitter_scalar (emitter, "module", YAML_PLAIN_SCALAR_STYLE, error))
1005     {
1006       return FALSE;
1007     }
1008 
1009   if (!mmd_emitter_scalar (emitter,
1010                            modulemd_obsoletes_get_obsoleted_by_module_name (
1011                              MODULEMD_OBSOLETES (self)),
1012                            YAML_PLAIN_SCALAR_STYLE,
1013                            error))
1014     {
1015       return FALSE;
1016     }
1017 
1018   /* The module stream is mandatory if already in obsoleted_by */
1019   if (!mmd_emitter_scalar (emitter, "stream", YAML_PLAIN_SCALAR_STYLE, error))
1020     {
1021       return FALSE;
1022     }
1023 
1024   if (!mmd_emitter_scalar (emitter,
1025                            modulemd_obsoletes_get_obsoleted_by_module_stream (
1026                              MODULEMD_OBSOLETES (self)),
1027                            YAML_DOUBLE_QUOTED_SCALAR_STYLE,
1028                            error))
1029     {
1030       return FALSE;
1031     }
1032 
1033   /* End the mapping for "obsoleted_by:" */
1034   if (!mmd_emitter_end_mapping (emitter, error))
1035     {
1036       return FALSE;
1037     }
1038   return TRUE;
1039 }
1040 
1041 
1042 gboolean
modulemd_obsoletes_emit_yaml(ModulemdObsoletes * self,yaml_emitter_t * emitter,GError ** error)1043 modulemd_obsoletes_emit_yaml (ModulemdObsoletes *self,
1044                               yaml_emitter_t *emitter,
1045                               GError **error)
1046 {
1047   MODULEMD_INIT_TRACE ();
1048   g_autoptr (GError) nested_error = NULL;
1049   guint64 eol_date;
1050   g_autofree gchar *modified_string = NULL;
1051   g_autofree gchar *eol_date_string = NULL;
1052   const gchar *module_context = NULL;
1053 
1054   if (!modulemd_obsoletes_validate (MODULEMD_OBSOLETES (self), &nested_error))
1055     {
1056       /* Validation failed */
1057       g_propagate_prefixed_error (error,
1058                                   g_steal_pointer (&nested_error),
1059                                   "Obsoletes object failed validation: ");
1060       return FALSE;
1061     }
1062 
1063   /* First emit the standard document headers */
1064   if (!modulemd_yaml_emit_document_headers (
1065         emitter,
1066         MODULEMD_YAML_DOC_OBSOLETES,
1067         modulemd_obsoletes_get_mdversion (MODULEMD_OBSOLETES (self)),
1068         error))
1069     {
1070       return FALSE;
1071     }
1072 
1073   /* Start the data: section mapping */
1074   if (!mmd_emitter_start_mapping (emitter, YAML_BLOCK_MAPPING_STYLE, error))
1075     {
1076       return FALSE;
1077     }
1078 
1079   /* Fill in the default data */
1080 
1081   /* The modified field is mandatory */
1082   if (!mmd_emitter_scalar (
1083         emitter, "modified", YAML_PLAIN_SCALAR_STYLE, error))
1084     {
1085       return FALSE;
1086     }
1087   modified_string = modulemd_guint64_to_iso8601date (
1088     modulemd_obsoletes_get_modified (MODULEMD_OBSOLETES (self)));
1089   if (!modified_string)
1090     {
1091       g_set_error (
1092         error,
1093         MODULEMD_ERROR,
1094         MMD_ERROR_VALIDATE,
1095         "Cannot convert modified date: %" PRIu64 " to iso8601 date.",
1096         modulemd_obsoletes_get_modified (MODULEMD_OBSOLETES (self)));
1097       return FALSE;
1098     }
1099   if (!mmd_emitter_scalar (
1100         emitter, modified_string, YAML_PLAIN_SCALAR_STYLE, error))
1101     {
1102       return FALSE;
1103     }
1104 
1105   /* Only output reset if it's TRUE */
1106   if (modulemd_obsoletes_get_reset (self))
1107     {
1108       EMIT_KEY_VALUE (emitter, error, "reset", "true");
1109     }
1110 
1111   /* The module name is mandatory */
1112   if (!mmd_emitter_scalar (emitter, "module", YAML_PLAIN_SCALAR_STYLE, error))
1113     {
1114       return FALSE;
1115     }
1116 
1117   if (!mmd_emitter_scalar (
1118         emitter,
1119         modulemd_obsoletes_get_module_name (MODULEMD_OBSOLETES (self)),
1120         YAML_PLAIN_SCALAR_STYLE,
1121         error))
1122     {
1123       return FALSE;
1124     }
1125 
1126   /* The module stream is mandatory */
1127   if (!mmd_emitter_scalar (emitter, "stream", YAML_PLAIN_SCALAR_STYLE, error))
1128     {
1129       return FALSE;
1130     }
1131 
1132   if (!mmd_emitter_scalar (
1133         emitter,
1134         modulemd_obsoletes_get_module_stream (MODULEMD_OBSOLETES (self)),
1135         YAML_DOUBLE_QUOTED_SCALAR_STYLE,
1136         error))
1137     {
1138       return FALSE;
1139     }
1140 
1141   /* The module context is optional */
1142   module_context =
1143     modulemd_obsoletes_get_module_context (MODULEMD_OBSOLETES (self));
1144   if (module_context)
1145     {
1146       if (!mmd_emitter_scalar (
1147             emitter, "context", YAML_PLAIN_SCALAR_STYLE, error))
1148         {
1149           return FALSE;
1150         }
1151 
1152       if (!mmd_emitter_scalar (
1153             emitter, module_context, YAML_PLAIN_SCALAR_STYLE, error))
1154         {
1155           return FALSE;
1156         }
1157     }
1158 
1159   /* The eol_date field is optional */
1160   eol_date = modulemd_obsoletes_get_eol_date (MODULEMD_OBSOLETES (self));
1161   if (eol_date)
1162     {
1163       eol_date_string = modulemd_guint64_to_iso8601date (eol_date);
1164       if (eol_date_string == NULL)
1165         {
1166           g_set_error (error,
1167                        MODULEMD_ERROR,
1168                        MMD_ERROR_VALIDATE,
1169                        "Cannot convert eol_date: %" PRIu64 " to iso8601 date.",
1170                        eol_date);
1171           return FALSE;
1172         }
1173       EMIT_KEY_VALUE (emitter, error, "eol_date", eol_date_string);
1174     }
1175 
1176   /* The message is mandatory */
1177   if (!mmd_emitter_scalar (emitter, "message", YAML_PLAIN_SCALAR_STYLE, error))
1178     {
1179       return FALSE;
1180     }
1181 
1182   if (!mmd_emitter_scalar (
1183         emitter,
1184         modulemd_obsoletes_get_message (MODULEMD_OBSOLETES (self)),
1185         YAML_PLAIN_SCALAR_STYLE,
1186         error))
1187     {
1188       return FALSE;
1189     }
1190 
1191   /* Obsoleted_by are optional */
1192   if (modulemd_obsoletes_get_obsoleted_by_module_name (
1193         MODULEMD_OBSOLETES (self)) &&
1194       modulemd_obsoletes_get_obsoleted_by_module_stream (
1195         MODULEMD_OBSOLETES (self)))
1196     {
1197       if (!modulemd_obsoletes_emit_obsoleted_by (self, emitter, error))
1198         {
1199           return FALSE;
1200         }
1201     }
1202 
1203   /* Close the data: section mapping */
1204   if (!mmd_emitter_end_mapping (emitter, error))
1205     {
1206       return FALSE;
1207     }
1208 
1209   /* Close the top-level section mapping */
1210   if (!mmd_emitter_end_mapping (emitter, error))
1211     {
1212       return FALSE;
1213     }
1214 
1215   /* End the document */
1216   if (!mmd_emitter_end_document (emitter, error))
1217     {
1218       return FALSE;
1219     }
1220 
1221   return TRUE;
1222 }
1223 
1224 gboolean
modulemd_obsoletes_is_active(ModulemdObsoletes * self)1225 modulemd_obsoletes_is_active (ModulemdObsoletes *self)
1226 {
1227   time_t rawtime;
1228   struct tm *tm;
1229   time (&rawtime);
1230   tm = gmtime (&rawtime);
1231 
1232   char buf[255];
1233   strftime (buf, sizeof (buf), "%Y%m%d%H%M", tm);
1234   guint64 now = g_ascii_strtoull (buf, NULL, 0);
1235 
1236   if (now >= modulemd_obsoletes_get_eol_date (self))
1237     {
1238       return TRUE;
1239     }
1240 
1241   return FALSE;
1242 }
1243