1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
2  *
3  * Copyright (C) 2016 Richard Hughes <richard@hughsie.com>
4  *
5  * SPDX-License-Identifier: LGPL-2.1+
6  */
7 
8 /**
9  * SECTION:as-require
10  * @short_description: Object representing a single requirement.
11  * @include: appstream-glib.h
12  * @stability: Unstable
13  *
14  * Requirements are things the component needs to be valid.
15  *
16  * See also: #AsApp
17  */
18 
19 #include "config.h"
20 
21 #include "as-require-private.h"
22 #include "as-node-private.h"
23 #include "as-ref-string.h"
24 #include "as-utils-private.h"
25 
26 #ifndef _WIN32
27 #include <fnmatch.h>
28 #endif
29 
30 typedef struct
31 {
32 	AsRequireKind		 kind;
33 	AsRequireCompare	 compare;
34 	AsRefString		*version;	/* utf8 */
35 	AsRefString		*value;	/* utf8 */
36 } AsRequirePrivate;
37 
G_DEFINE_TYPE_WITH_PRIVATE(AsRequire,as_require,G_TYPE_OBJECT)38 G_DEFINE_TYPE_WITH_PRIVATE (AsRequire, as_require, G_TYPE_OBJECT)
39 
40 #define GET_PRIVATE(o) (as_require_get_instance_private (o))
41 
42 static void
43 as_require_finalize (GObject *object)
44 {
45 	AsRequire *require = AS_REQUIRE (object);
46 	AsRequirePrivate *priv = GET_PRIVATE (require);
47 
48 	if (priv->version != NULL)
49 		as_ref_string_unref (priv->version);
50 	if (priv->value != NULL)
51 		as_ref_string_unref (priv->value);
52 
53 	G_OBJECT_CLASS (as_require_parent_class)->finalize (object);
54 }
55 
56 static void
as_require_init(AsRequire * require)57 as_require_init (AsRequire *require)
58 {
59 //	AsRequirePrivate *priv = GET_PRIVATE (require);
60 }
61 
62 static void
as_require_class_init(AsRequireClass * klass)63 as_require_class_init (AsRequireClass *klass)
64 {
65 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
66 	object_class->finalize = as_require_finalize;
67 }
68 
69 /**
70  * as_require_kind_from_string:
71  * @kind: the string.
72  *
73  * Converts the text representation to an enumerated value.
74  *
75  * Returns: (transfer full): a #AsRequireKind, or %AS_REQUIRE_KIND_UNKNOWN for unknown.
76  *
77  * Since: 0.6.7
78  **/
79 AsRequireKind
as_require_kind_from_string(const gchar * kind)80 as_require_kind_from_string (const gchar *kind)
81 {
82 	if (g_strcmp0 (kind, "id") == 0)
83 		return AS_REQUIRE_KIND_ID;
84 	if (g_strcmp0 (kind, "firmware") == 0)
85 		return AS_REQUIRE_KIND_FIRMWARE;
86 	if (g_strcmp0 (kind, "hardware") == 0)
87 		return AS_REQUIRE_KIND_HARDWARE;
88 	if (g_strcmp0 (kind, "modalias") == 0)
89 		return AS_REQUIRE_KIND_MODALIAS;
90 	if (g_strcmp0 (kind, "kernel") == 0)
91 		return AS_REQUIRE_KIND_KERNEL;
92 	if (g_strcmp0 (kind, "memory") == 0)
93 		return AS_REQUIRE_KIND_MEMORY;
94 	return AS_REQUIRE_KIND_UNKNOWN;
95 }
96 
97 /**
98  * as_require_kind_to_string:
99  * @kind: the #AsRequireKind.
100  *
101  * Converts the enumerated value to an text representation.
102  *
103  * Returns: string version of @kind
104  *
105  * Since: 0.6.7
106  **/
107 const gchar *
as_require_kind_to_string(AsRequireKind kind)108 as_require_kind_to_string (AsRequireKind kind)
109 {
110 	if (kind == AS_REQUIRE_KIND_ID)
111 		return "id";
112 	if (kind == AS_REQUIRE_KIND_FIRMWARE)
113 		return "firmware";
114 	if (kind == AS_REQUIRE_KIND_HARDWARE)
115 		return "hardware";
116 	if (kind == AS_REQUIRE_KIND_MODALIAS)
117 		return "modalias";
118 	if (kind == AS_REQUIRE_KIND_KERNEL)
119 		return "kernel";
120 	if (kind == AS_REQUIRE_KIND_MEMORY)
121 		return "memory";
122 	return NULL;
123 }
124 
125 /**
126  * as_require_compare_from_string:
127  * @compare: the string.
128  *
129  * Converts the text representation to an enumerated value.
130  *
131  * Returns: (transfer full): a #AsRequireCompare, or
132  *			     %AS_REQUIRE_COMPARE_UNKNOWN for unknown.
133  *
134  * Since: 0.6.7
135  **/
136 AsRequireCompare
as_require_compare_from_string(const gchar * compare)137 as_require_compare_from_string (const gchar *compare)
138 {
139 	if (g_strcmp0 (compare, "eq") == 0)
140 		return AS_REQUIRE_COMPARE_EQ;
141 	if (g_strcmp0 (compare, "ne") == 0)
142 		return AS_REQUIRE_COMPARE_NE;
143 	if (g_strcmp0 (compare, "gt") == 0)
144 		return AS_REQUIRE_COMPARE_GT;
145 	if (g_strcmp0 (compare, "lt") == 0)
146 		return AS_REQUIRE_COMPARE_LT;
147 	if (g_strcmp0 (compare, "ge") == 0)
148 		return AS_REQUIRE_COMPARE_GE;
149 	if (g_strcmp0 (compare, "le") == 0)
150 		return AS_REQUIRE_COMPARE_LE;
151 	if (g_strcmp0 (compare, "glob") == 0)
152 		return AS_REQUIRE_COMPARE_GLOB;
153 	if (g_strcmp0 (compare, "regex") == 0)
154 		return AS_REQUIRE_COMPARE_REGEX;
155 	return AS_REQUIRE_COMPARE_UNKNOWN;
156 }
157 
158 /**
159  * as_require_compare_to_string:
160  * @compare: the #AsRequireCompare.
161  *
162  * Converts the enumerated value to an text representation.
163  *
164  * Returns: string version of @compare
165  *
166  * Since: 0.6.7
167  **/
168 const gchar *
as_require_compare_to_string(AsRequireCompare compare)169 as_require_compare_to_string (AsRequireCompare compare)
170 {
171 	if (compare == AS_REQUIRE_COMPARE_EQ)
172 		return "eq";
173 	if (compare == AS_REQUIRE_COMPARE_NE)
174 		return "ne";
175 	if (compare == AS_REQUIRE_COMPARE_GT)
176 		return "gt";
177 	if (compare == AS_REQUIRE_COMPARE_LT)
178 		return "lt";
179 	if (compare == AS_REQUIRE_COMPARE_GE)
180 		return "ge";
181 	if (compare == AS_REQUIRE_COMPARE_LE)
182 		return "le";
183 	if (compare == AS_REQUIRE_COMPARE_GLOB)
184 		return "glob";
185 	if (compare == AS_REQUIRE_COMPARE_REGEX)
186 		return "regex";
187 	return NULL;
188 }
189 
190 /**
191  * as_require_get_version:
192  * @require: a #AsRequire instance.
193  *
194  * Gets the require version if set.
195  *
196  * Returns: the version, e.g. "0.1.2"
197  *
198  * Since: 0.6.7
199  **/
200 const gchar *
as_require_get_version(AsRequire * require)201 as_require_get_version (AsRequire *require)
202 {
203 	AsRequirePrivate *priv = GET_PRIVATE (require);
204 	g_return_val_if_fail (AS_IS_REQUIRE (require), NULL);
205 	return priv->version;
206 }
207 
208 /**
209  * as_require_get_value:
210  * @require: a #AsRequire instance.
211  *
212  * Gets the require value if set.
213  *
214  * Returns: the value, e.g. "bootloader"
215  *
216  * Since: 0.6.7
217  **/
218 const gchar *
as_require_get_value(AsRequire * require)219 as_require_get_value (AsRequire *require)
220 {
221 	AsRequirePrivate *priv = GET_PRIVATE (require);
222 	g_return_val_if_fail (AS_IS_REQUIRE (require), NULL);
223 	return priv->value;
224 }
225 
226 /**
227  * as_require_get_kind:
228  * @require: a #AsRequire instance.
229  *
230  * Gets the require kind.
231  *
232  * Returns: the #AsRequireKind
233  *
234  * Since: 0.6.7
235  **/
236 AsRequireKind
as_require_get_kind(AsRequire * require)237 as_require_get_kind (AsRequire *require)
238 {
239 	AsRequirePrivate *priv = GET_PRIVATE (require);
240 	g_return_val_if_fail (AS_IS_REQUIRE (require), AS_REQUIRE_KIND_UNKNOWN);
241 	return priv->kind;
242 }
243 
244 /**
245  * as_require_set_kind:
246  * @require: a #AsRequire instance.
247  * @kind: the #AsRequireKind, e.g. %AS_REQUIRE_KIND_ID.
248  *
249  * Sets the require kind.
250  *
251  * Since: 0.6.7
252  **/
253 void
as_require_set_kind(AsRequire * require,AsRequireKind kind)254 as_require_set_kind (AsRequire *require, AsRequireKind kind)
255 {
256 	AsRequirePrivate *priv = GET_PRIVATE (require);
257 	g_return_if_fail (AS_IS_REQUIRE (require));
258 	priv->kind = kind;
259 }
260 
261 /**
262  * as_require_get_compare:
263  * @require: a #AsRequire instance.
264  *
265  * Gets the require version comparison type.
266  *
267  * Returns: the #AsRequireKind
268  *
269  * Since: 0.6.7
270  **/
271 AsRequireCompare
as_require_get_compare(AsRequire * require)272 as_require_get_compare (AsRequire *require)
273 {
274 	AsRequirePrivate *priv = GET_PRIVATE (require);
275 	g_return_val_if_fail (AS_IS_REQUIRE (require), AS_REQUIRE_COMPARE_UNKNOWN);
276 	return priv->compare;
277 }
278 
279 /**
280  * as_require_set_compare:
281  * @require: a #AsRequire instance.
282  * @compare: the #AsRequireKind, e.g. %AS_REQUIRE_KIND_ID.
283  *
284  * Sets the require version comparison type.
285  *
286  * Since: 0.6.7
287  **/
288 void
as_require_set_compare(AsRequire * require,AsRequireCompare compare)289 as_require_set_compare (AsRequire *require, AsRequireCompare compare)
290 {
291 	AsRequirePrivate *priv = GET_PRIVATE (require);
292 	g_return_if_fail (AS_IS_REQUIRE (require));
293 	priv->compare = compare;
294 }
295 
296 /**
297  * as_require_set_version:
298  * @require: a #AsRequire instance.
299  * @version: an version number, e.g. `0.1.2`
300  *
301  * Sets the require version.
302  *
303  * Since: 0.6.7
304  **/
305 void
as_require_set_version(AsRequire * require,const gchar * version)306 as_require_set_version (AsRequire *require, const gchar *version)
307 {
308 	AsRequirePrivate *priv = GET_PRIVATE (require);
309 	g_return_if_fail (AS_IS_REQUIRE (require));
310 	if (priv->version != NULL)
311 		as_ref_string_unref (priv->version);
312 	priv->version = as_ref_string_new (version);
313 }
314 
315 /**
316  * as_require_set_value:
317  * @require: a #AsRequire instance.
318  * @value: an require version, e.g. `firmware`
319  *
320  * Sets the require value.
321  *
322  * Since: 0.6.7
323  **/
324 void
as_require_set_value(AsRequire * require,const gchar * value)325 as_require_set_value (AsRequire *require, const gchar *value)
326 {
327 	AsRequirePrivate *priv = GET_PRIVATE (require);
328 	g_return_if_fail (AS_IS_REQUIRE (require));
329 	if (priv->value != NULL)
330 		as_ref_string_unref (priv->value);
331 	priv->value = as_ref_string_new (value);
332 }
333 
334 
335 /**
336  * as_require_version_compare:
337  * @require: a #AsRequire instance.
338  * @version: a version number, e.g. `0.1.3`
339  * @error: A #GError or %NULL
340  *
341  * Compares the version number of the requirement with a predicate.
342  *
343  * Returns: %TRUE if the predicate was true
344  *
345  * Since: 0.6.7
346  **/
347 gboolean
as_require_version_compare(AsRequire * require,const gchar * version,GError ** error)348 as_require_version_compare (AsRequire *require,
349 			    const gchar *version,
350 			    GError **error)
351 {
352 	AsRequirePrivate *priv = GET_PRIVATE (require);
353 	gboolean ret = FALSE;
354 	gint rc = 0;
355 
356 	g_return_val_if_fail (AS_IS_REQUIRE (require), FALSE);
357 
358 	switch (priv->compare) {
359 	case AS_REQUIRE_COMPARE_EQ:
360 		rc = as_utils_vercmp_full (version, priv->version, AS_VERSION_COMPARE_FLAG_NONE);
361 		ret = rc == 0;
362 		break;
363 	case AS_REQUIRE_COMPARE_NE:
364 		rc = as_utils_vercmp_full (version, priv->version, AS_VERSION_COMPARE_FLAG_NONE);
365 		ret = rc != 0;
366 		break;
367 	case AS_REQUIRE_COMPARE_LT:
368 		rc = as_utils_vercmp_full (version, priv->version, AS_VERSION_COMPARE_FLAG_NONE);
369 		ret = rc < 0;
370 		break;
371 	case AS_REQUIRE_COMPARE_GT:
372 		rc = as_utils_vercmp_full (version, priv->version, AS_VERSION_COMPARE_FLAG_NONE);
373 		ret = rc > 0;
374 		break;
375 	case AS_REQUIRE_COMPARE_LE:
376 		rc = as_utils_vercmp_full (version, priv->version, AS_VERSION_COMPARE_FLAG_NONE);
377 		ret = rc <= 0;
378 		break;
379 	case AS_REQUIRE_COMPARE_GE:
380 		rc = as_utils_vercmp_full (version, priv->version, AS_VERSION_COMPARE_FLAG_NONE);
381 		ret = rc >= 0;
382 		break;
383 	case AS_REQUIRE_COMPARE_GLOB:
384 #ifdef _WIN32
385 		ret = g_strcmp0 (priv->version, version) == 0;
386 #else
387 		ret = fnmatch (priv->version, version, 0) == 0;
388 #endif
389 		break;
390 	case AS_REQUIRE_COMPARE_REGEX:
391 		ret = g_regex_match_simple (priv->version, version, 0, 0);
392 		break;
393 	default:
394 		break;
395 	}
396 
397 	/* could not compare */
398 	if (rc == G_MAXINT) {
399 		g_set_error (error,
400 			     AS_UTILS_ERROR,
401 			     AS_UTILS_ERROR_FAILED,
402 			     "failed to compare [%s] and [%s]",
403 			     priv->version,
404 			     version);
405 		return FALSE;
406 	}
407 
408 	/* set error */
409 	if (!ret && error != NULL) {
410 		g_set_error (error,
411 			     AS_UTILS_ERROR,
412 			     AS_UTILS_ERROR_FAILED,
413 			     "failed predicate [%s %s %s]",
414 			     priv->version,
415 			     as_require_compare_to_string (priv->compare),
416 			     version);
417 	}
418 	return ret;
419 }
420 
421 /**
422  * as_require_equal:
423  * @require1: a #AsRequire instance.
424  * @require2: a #AsRequire instance.
425  *
426  * Checks if two requires are the same.
427  *
428  * Returns: %TRUE for success
429  *
430  * Since: 0.7.7
431  **/
432 gboolean
as_require_equal(AsRequire * require1,AsRequire * require2)433 as_require_equal (AsRequire *require1, AsRequire *require2)
434 {
435 	AsRequirePrivate *priv1 = GET_PRIVATE (require1);
436 	AsRequirePrivate *priv2 = GET_PRIVATE (require2);
437 
438 	g_return_val_if_fail (AS_IS_REQUIRE (require1), FALSE);
439 	g_return_val_if_fail (AS_IS_REQUIRE (require2), FALSE);
440 
441 	/* trivial */
442 	if (require1 == require2)
443 		return TRUE;
444 
445 	/* check for equality */
446 	if (priv1->kind != priv2->kind)
447 		return FALSE;
448 	if (priv1->compare != priv2->compare)
449 		return FALSE;
450 	if (g_strcmp0 (priv1->version, priv2->version) != 0)
451 		return FALSE;
452 	if (g_strcmp0 (priv1->value, priv2->value) != 0)
453 		return FALSE;
454 
455 	/* success */
456 	return TRUE;
457 }
458 
459 /**
460  * as_require_node_insert: (skip)
461  * @require: a #AsRequire instance.
462  * @parent: the parent #GNode to use..
463  * @ctx: the #AsNodeContext
464  *
465  * Inserts the require into the DOM tree.
466  *
467  * Returns: (transfer none): A populated #GNode
468  *
469  * Since: 0.6.7
470  **/
471 GNode *
as_require_node_insert(AsRequire * require,GNode * parent,AsNodeContext * ctx)472 as_require_node_insert (AsRequire *require, GNode *parent, AsNodeContext *ctx)
473 {
474 	AsRequirePrivate *priv = GET_PRIVATE (require);
475 	GNode *n;
476 
477 	g_return_val_if_fail (AS_IS_REQUIRE (require), NULL);
478 
479 	/* don't know what to do here */
480 	if (priv->kind == AS_REQUIRE_KIND_UNKNOWN)
481 		return NULL;
482 
483 	n = as_node_insert (parent, as_require_kind_to_string (priv->kind), NULL,
484 			    AS_NODE_INSERT_FLAG_NONE,
485 			    NULL);
486 	if (priv->compare != AS_REQUIRE_COMPARE_UNKNOWN) {
487 		as_node_add_attribute (n, "compare",
488 				       as_require_compare_to_string (priv->compare));
489 	}
490 	if (priv->version != NULL)
491 		as_node_add_attribute (n, "version", priv->version);
492 	if (priv->value != NULL)
493 		as_node_set_data (n, priv->value, AS_NODE_INSERT_FLAG_NONE);
494 	return n;
495 }
496 
497 /**
498  * as_require_node_parse:
499  * @require: a #AsRequire instance.
500  * @node: a #GNode.
501  * @ctx: a #AsNodeContext.
502  * @error: A #GError or %NULL.
503  *
504  * Populates the object from a DOM node.
505  *
506  * Returns: %TRUE for success
507  *
508  * Since: 0.6.7
509  **/
510 gboolean
as_require_node_parse(AsRequire * require,GNode * node,AsNodeContext * ctx,GError ** error)511 as_require_node_parse (AsRequire *require, GNode *node,
512 		       AsNodeContext *ctx, GError **error)
513 {
514 	AsRequirePrivate *priv = GET_PRIVATE (require);
515 	const gchar *tmp;
516 	g_return_val_if_fail (AS_IS_REQUIRE (require), FALSE);
517 	tmp = as_node_get_name (node);
518 	if (tmp != NULL)
519 		as_require_set_kind (require, as_require_kind_from_string (tmp));
520 	tmp = as_node_get_attribute (node, "compare");
521 	if (tmp != NULL)
522 		as_require_set_compare (require, as_require_compare_from_string (tmp));
523 	as_ref_string_assign (&priv->version, as_node_get_attribute_as_refstr (node, "version"));
524 	as_ref_string_assign (&priv->value, as_node_get_data_as_refstr (node));
525 	return TRUE;
526 }
527 
528 /**
529  * as_require_node_parse_dep11:
530  * @require: a #AsRequire instance.
531  * @node: a #GNode.
532  * @ctx: a #AsNodeContext.
533  * @error: A #GError or %NULL.
534  *
535  * Populates the object from a DEP-11 node.
536  *
537  * Returns: %TRUE for success
538  *
539  * Since: 0.6.7
540  **/
541 gboolean
as_require_node_parse_dep11(AsRequire * im,GNode * node,AsNodeContext * ctx,GError ** error)542 as_require_node_parse_dep11 (AsRequire *im, GNode *node,
543 			     AsNodeContext *ctx, GError **error)
544 {
545 	return TRUE;
546 }
547 
548 /**
549  * as_require_new:
550  *
551  * Creates a new #AsRequire.
552  *
553  * Returns: (transfer full): a #AsRequire
554  *
555  * Since: 0.6.7
556  **/
557 AsRequire *
as_require_new(void)558 as_require_new (void)
559 {
560 	AsRequire *require;
561 	require = g_object_new (AS_TYPE_REQUIRE, NULL);
562 	return AS_REQUIRE (require);
563 }
564