1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Copyright (C) 2016 Red Hat, Inc. (www.redhat.com)
4  *
5  * This library is free software: you can redistribute it and/or modify it
6  * under the terms of the GNU Lesser General Public License as published by
7  * the Free Software Foundation.
8  *
9  * This library is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
12  * for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public License
15  * along with this library. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include "evolution-data-server-config.h"
19 
20 #include <stdio.h>
21 #include <string.h>
22 
23 #include "camel-string-utils.h"
24 
25 #include "camel-name-value-array.h"
26 
27 G_DEFINE_BOXED_TYPE (CamelNameValueArray,
28 		camel_name_value_array,
29 		camel_name_value_array_copy,
30 		camel_name_value_array_free)
31 
32 typedef struct _CamelNameValuePair {
33 	gchar *name;
34 	gchar *value;
35 } CamelNameValuePair;
36 
37 static void
free_name_value_content(gpointer ptr)38 free_name_value_content (gpointer ptr)
39 {
40 	CamelNameValuePair *pair = ptr;
41 
42 	if (pair) {
43 		g_free (pair->name);
44 		g_free (pair->value);
45 
46 		pair->name = NULL;
47 		pair->value = NULL;
48 	}
49 }
50 
51 /**
52  * camel_name_value_array_new:
53  *
54  * Creates a new #CamelNameValueArray. The returned pointer should be freed
55  * with camel_name_value_array_free() when no longer needed.
56  *
57  * Returns: (transfer full): A new #CamelNameValueArray.
58  *
59  * See: camel_name_value_array_new_sized, camel_name_value_array_copy
60  *
61  * Since: 3.24
62  **/
63 CamelNameValueArray *
camel_name_value_array_new(void)64 camel_name_value_array_new (void)
65 {
66 	GArray *arr;
67 
68 	arr = g_array_new (FALSE, FALSE, sizeof (CamelNameValuePair));
69 	g_array_set_clear_func (arr, free_name_value_content);
70 
71 	return (CamelNameValueArray *) arr;
72 }
73 
74 /**
75  * camel_name_value_array_new_sized:
76  * @reserve_size: an array size to reserve
77  *
78  * Creates a new #CamelNameValueArray, which has reserved @reserve_size
79  * elements. This value doesn't influence the camel_name_value_array_get_length(),
80  * which returns zero on the array returned from this function. The returned
81  * pointer should be freed with camel_name_value_array_free() when no longer needed.
82  *
83  * Returns: (transfer full): A new #CamelNameValueArray.
84  *
85  * See: camel_name_value_array_new, camel_name_value_array_copy
86  *
87  * Since: 3.24
88  **/
89 CamelNameValueArray *
camel_name_value_array_new_sized(guint reserve_size)90 camel_name_value_array_new_sized (guint reserve_size)
91 {
92 	GArray *arr;
93 
94 	arr = g_array_sized_new (FALSE, FALSE, sizeof (CamelNameValuePair), reserve_size);
95 	g_array_set_clear_func (arr, free_name_value_content);
96 
97 	return (CamelNameValueArray *) arr;
98 }
99 
100 /**
101  * camel_name_value_array_copy:
102  * @array: (nullable): a #CamelNameValueArray
103  *
104  * Creates a new copy of the @array. The returned pointer should be freed
105  * with camel_name_value_array_free() when no longer needed.
106  *
107  * Returns: (transfer full): A new copy of the @array.
108  *
109  * See: camel_name_value_array_new, camel_name_value_array_new_sized
110  *
111  * Since: 3.24
112  **/
113 CamelNameValueArray *
camel_name_value_array_copy(const CamelNameValueArray * array)114 camel_name_value_array_copy (const CamelNameValueArray *array)
115 {
116 	CamelNameValueArray *copy;
117 	guint ii, len;
118 
119 	if (!array)
120 		return NULL;
121 
122 	len = camel_name_value_array_get_length (array);
123 	copy = camel_name_value_array_new_sized (len);
124 
125 	for (ii = 0; ii < len; ii++) {
126 		const gchar *name = NULL, *value = NULL;
127 
128 		if (camel_name_value_array_get (array, ii, &name, &value))
129 			camel_name_value_array_append (copy, name, value);
130 	}
131 
132 	return copy;
133 }
134 
135 /**
136  * camel_name_value_array_free:
137  * @array: (nullable): a #CamelNameValueArray, or %NULL
138  *
139  * Frees the @array, previously allocated by camel_name_value_array_new(),
140  * camel_name_value_array_new_sized() or camel_name_value_array_copy().
141  * If the @array is %NULL, then does nothing.
142  *
143  * Since: 3.24
144  **/
145 void
camel_name_value_array_free(CamelNameValueArray * array)146 camel_name_value_array_free (CamelNameValueArray *array)
147 {
148 	if (array)
149 		g_array_free ((GArray *) array, TRUE);
150 }
151 
152 /**
153  * camel_name_value_array_get_length:
154  * @array: (nullable): a #CamelNameValueArray
155  *
156  * Returns: Length of the @array, aka how many elements are stored in the @array.
157  *
158  * Since: 3.24
159  **/
160 guint
camel_name_value_array_get_length(const CamelNameValueArray * array)161 camel_name_value_array_get_length (const CamelNameValueArray *array)
162 {
163 	GArray *arr = (GArray *) array;
164 
165 	if (!array)
166 		return 0;
167 
168 	return arr->len;
169 }
170 
171 /**
172  * camel_name_value_array_get:
173  * @array: a #CamelNameValueArray
174  * @index: an index
175  * @out_name: (out) (nullable): A place to store the name of the element, or %NULL
176  * @out_value: (out) (nullable): A place to store the value of the element, or %NULL
177  *
178  * Returns the name and the value of the element at index @index. Either
179  * of the @out_name and @out_value can be %NULL, to not return that part.
180  *
181  * Returns: %TRUE on success, %FALSE otherwise.
182  *
183  * See: camel_name_value_array_get_name, camel_name_value_array_get_value, camel_name_value_array_get_named
184  *
185  * Since: 3.24
186  **/
187 gboolean
camel_name_value_array_get(const CamelNameValueArray * array,guint index,const gchar ** out_name,const gchar ** out_value)188 camel_name_value_array_get (const CamelNameValueArray *array,
189 			    guint index,
190 			    const gchar **out_name,
191 			    const gchar **out_value)
192 {
193 	GArray *arr = (GArray *) array;
194 	CamelNameValuePair *pair;
195 
196 	g_return_val_if_fail (array != NULL, FALSE);
197 
198 	if (index >= camel_name_value_array_get_length (array))
199 		return FALSE;
200 
201 	pair = &g_array_index (arr, CamelNameValuePair, index);
202 
203 	if (out_name)
204 		*out_name = pair->name;
205 	if (out_value)
206 		*out_value= pair->value;
207 
208 	return TRUE;
209 }
210 
211 static guint
camel_name_value_array_find_named(const CamelNameValueArray * array,CamelCompareType compare_type,const gchar * name)212 camel_name_value_array_find_named (const CamelNameValueArray *array,
213 				   CamelCompareType compare_type,
214 				   const gchar *name)
215 {
216 	GArray *arr = (GArray *) array;
217 	gboolean case_sensitive;
218 	gint ii;
219 
220 	g_return_val_if_fail (array != NULL, (guint) -1);
221 	g_return_val_if_fail (name != NULL, (guint) -1);
222 
223 	case_sensitive = compare_type == CAMEL_COMPARE_CASE_SENSITIVE;
224 
225 	for (ii = 0; ii < arr->len; ii++) {
226 		CamelNameValuePair *pair = &g_array_index (arr, CamelNameValuePair, ii);
227 
228 		if ((case_sensitive && g_strcmp0 (name, pair->name) == 0) ||
229 		    (!case_sensitive && pair->name && camel_strcase_equal (name, pair->name))) {
230 			return ii;
231 		}
232 	}
233 
234 	return (guint) -1;
235 }
236 
237 /**
238  * camel_name_value_array_get_named:
239  * @array: a #CamelNameValueArray
240  * @compare_type: a compare type, one of #CamelCompareType
241  * @name: a name
242  *
243  * Returns the value of the first element named @name, or %NULL when there
244  * is no element of such @name in the @array. The @compare_type determines
245  * how to compare the names.
246  *
247  * Returns: (transfer none) (nullable): Value of the first element named @name, or %NULL.
248  *
249  * See: camel_name_value_array_get, camel_name_value_array_get_name
250  *
251  * Since: 3.24
252  **/
253 const gchar *
camel_name_value_array_get_named(const CamelNameValueArray * array,CamelCompareType compare_type,const gchar * name)254 camel_name_value_array_get_named (const CamelNameValueArray *array,
255 				  CamelCompareType compare_type,
256 				  const gchar *name)
257 {
258 	guint index;
259 
260 	g_return_val_if_fail (array != NULL, NULL);
261 	g_return_val_if_fail (name != NULL, NULL);
262 
263 	index = camel_name_value_array_find_named (array, compare_type, name);
264 	if (index == (guint) -1)
265 		return NULL;
266 
267 	return camel_name_value_array_get_value (array, index);
268 }
269 
270 /**
271  * camel_name_value_array_get_name:
272  * @array: a #CamelNameValueArray
273  * @index: an index
274  *
275  * Returns the name of the element at index @index.
276  *
277  * Returns: (transfer none) (nullable): Name of the element at the given @index,
278  *    or %NULL on error.
279  *
280  * See: camel_name_value_array_get, camel_name_value_array_get_value
281  *
282  * Since: 3.24
283  **/
284 const gchar *
camel_name_value_array_get_name(const CamelNameValueArray * array,guint index)285 camel_name_value_array_get_name (const CamelNameValueArray *array,
286 				 guint index)
287 {
288 	const gchar *name = NULL;
289 
290 	g_return_val_if_fail (array != NULL, NULL);
291 
292 	if (!camel_name_value_array_get (array, index, &name, NULL))
293 		return NULL;
294 
295 	return name;
296 }
297 
298 /**
299  * camel_name_value_array_get_value:
300  * @array: a #CamelNameValueArray
301  * @index: an index
302  *
303  * Returns the value of the element at index @index.
304  *
305  * Returns: (transfer none) (nullable): Value of the element at the given @index,
306  *    or %NULL on error.
307  *
308  * See: camel_name_value_array_get, camel_name_value_array_get_name
309  *
310  * Since: 3.24
311  **/
312 const gchar *
camel_name_value_array_get_value(const CamelNameValueArray * array,guint index)313 camel_name_value_array_get_value (const CamelNameValueArray *array,
314 				  guint index)
315 {
316 	const gchar *value = NULL;
317 
318 	g_return_val_if_fail (array != NULL, NULL);
319 
320 	if (!camel_name_value_array_get (array, index, NULL, &value))
321 		return NULL;
322 
323 	return value;
324 }
325 
326 /**
327  * camel_name_value_array_append:
328  * @array: a #CamelNameValueArray
329  * @name: a name
330  * @value: a value
331  *
332  * Appends a new element of the name @name and the value @value
333  * at the end of @array.
334  *
335  * See: camel_name_value_array_set_named
336  *
337  * Since: 3.24
338  **/
339 void
camel_name_value_array_append(CamelNameValueArray * array,const gchar * name,const gchar * value)340 camel_name_value_array_append (CamelNameValueArray *array,
341 			       const gchar *name,
342 			       const gchar *value)
343 {
344 	GArray *arr = (GArray *) array;
345 	CamelNameValuePair pair;
346 
347 	g_return_if_fail (array != NULL);
348 	g_return_if_fail (name != NULL);
349 	g_return_if_fail (value != NULL);
350 
351 	pair.name = g_strdup (name);
352 	pair.value = g_strdup (value);
353 
354 	g_array_append_val (arr, pair);
355 }
356 
357 static gboolean
camel_name_value_array_set_internal(CamelNameValueArray * array,guint index,const gchar * name,const gchar * value)358 camel_name_value_array_set_internal (CamelNameValueArray *array,
359 				     guint index,
360 				     const gchar *name,
361 				     const gchar *value)
362 {
363 	GArray *arr = (GArray *) array;
364 	CamelNameValuePair *pair;
365 	gboolean changed = FALSE;
366 
367 	g_return_val_if_fail (array != NULL, FALSE);
368 	g_return_val_if_fail (index < camel_name_value_array_get_length (array), FALSE);
369 
370 	pair = &g_array_index (arr, CamelNameValuePair, index);
371 
372 	if (name && g_strcmp0 (pair->name, name) != 0) {
373 		g_free (pair->name);
374 		pair->name = g_strdup (name);
375 		changed = TRUE;
376 	}
377 
378 	if (value && g_strcmp0 (pair->value, value) != 0) {
379 		g_free (pair->value);
380 		pair->value = g_strdup (value);
381 		changed = TRUE;
382 	}
383 
384 	return changed;
385 }
386 
387 /**
388  * camel_name_value_array_set:
389  * @array: a #CamelNameValueArray
390  * @index: an index
391  * @name: a name
392  * @value: a value
393  *
394  * Sets both the @name and the @value of the element at index @index.
395  *
396  * Returns: Whether the @array changed.
397  *
398  * See: camel_name_value_array_append, camel_name_value_array_set_name, camel_name_value_array_set_value
399  *
400  * Since: 3.24
401  **/
402 gboolean
camel_name_value_array_set(CamelNameValueArray * array,guint index,const gchar * name,const gchar * value)403 camel_name_value_array_set (CamelNameValueArray *array,
404 			    guint index,
405 			    const gchar *name,
406 			    const gchar *value)
407 {
408 	g_return_val_if_fail (array != NULL, FALSE);
409 	g_return_val_if_fail (index < camel_name_value_array_get_length (array), FALSE);
410 	g_return_val_if_fail (name != NULL, FALSE);
411 	g_return_val_if_fail (value != NULL, FALSE);
412 
413 	return camel_name_value_array_set_internal (array, index, name, value);
414 }
415 
416 /**
417  * camel_name_value_array_set_name:
418  * @array: a #CamelNameValueArray
419  * @index: an index
420  * @name: a name
421  *
422  * Sets the @name of the element at index @index.
423  *
424  * Returns: Whether the @array changed.
425  *
426  * See: camel_name_value_array_set, camel_name_value_array_set_value
427  *
428  * Since: 3.24
429  **/
430 gboolean
camel_name_value_array_set_name(CamelNameValueArray * array,guint index,const gchar * name)431 camel_name_value_array_set_name (CamelNameValueArray *array,
432 				 guint index,
433 				 const gchar *name)
434 {
435 	g_return_val_if_fail (array != NULL, FALSE);
436 	g_return_val_if_fail (index < camel_name_value_array_get_length (array), FALSE);
437 	g_return_val_if_fail (name != NULL, FALSE);
438 
439 	return camel_name_value_array_set_internal (array, index, name, NULL);
440 }
441 
442 /**
443  * camel_name_value_array_set_value:
444  * @array: a #CamelNameValueArray
445  * @index: an index
446  * @value: a value
447  *
448  * Sets the @value of the element at index @index.
449  *
450  * Returns: Whether the @array changed.
451  *
452  * See: camel_name_value_array_set, camel_name_value_array_set_name
453  *
454  * Since: 3.24
455  **/
456 gboolean
camel_name_value_array_set_value(CamelNameValueArray * array,guint index,const gchar * value)457 camel_name_value_array_set_value (CamelNameValueArray *array,
458 				  guint index,
459 				  const gchar *value)
460 {
461 	g_return_val_if_fail (array != NULL, FALSE);
462 	g_return_val_if_fail (index < camel_name_value_array_get_length (array), FALSE);
463 	g_return_val_if_fail (value != NULL, FALSE);
464 
465 	return camel_name_value_array_set_internal (array, index, NULL, value);
466 }
467 
468 /**
469  * camel_name_value_array_set_named:
470  * @array: a #CamelNameValueArray
471  * @compare_type: a compare type, one of #CamelCompareType
472  * @name: a name
473  * @value: a value
474  *
475  * Finds an element named @name and sets its value to @value, or appends
476  * a new element, in case no such named element exists in the @array yet.
477  * In case there are more elements named with @name only the first
478  * occurrence is changed. The @compare_type determines how to compare
479  * the names.
480  *
481  * Returns: Whether the @array changed.
482  *
483  * See: camel_name_value_array_append, camel_name_value_array_set
484  *
485  * Since: 3.24
486  **/
487 gboolean
camel_name_value_array_set_named(CamelNameValueArray * array,CamelCompareType compare_type,const gchar * name,const gchar * value)488 camel_name_value_array_set_named (CamelNameValueArray *array,
489 				  CamelCompareType compare_type,
490 				  const gchar *name,
491 				  const gchar *value)
492 {
493 	gboolean changed = FALSE;
494 	guint index;
495 
496 	g_return_val_if_fail (array != NULL, FALSE);
497 	g_return_val_if_fail (name != NULL, FALSE);
498 	g_return_val_if_fail (value != NULL, FALSE);
499 
500 	index = camel_name_value_array_find_named (array, compare_type, name);
501 	if (index == (guint) -1) {
502 		camel_name_value_array_append (array, name, value);
503 		changed = TRUE;
504 	} else {
505 		changed = camel_name_value_array_set_value (array, index, value);
506 	}
507 
508 	return changed;
509 }
510 
511 /**
512  * camel_name_value_array_remove:
513  * @array: a #CamelNameValueArray
514  * @index: an index to remove
515  *
516  * Removes element at index @index.
517  *
518  * Returns: Whether the element was removed.
519  *
520  * Since: 3.24
521  **/
522 gboolean
camel_name_value_array_remove(CamelNameValueArray * array,guint index)523 camel_name_value_array_remove (CamelNameValueArray *array,
524 			       guint index)
525 {
526 	g_return_val_if_fail (array != NULL, FALSE);
527 	g_return_val_if_fail (index < camel_name_value_array_get_length (array), FALSE);
528 
529 	g_array_remove_index ((GArray *) array, index);
530 
531 	return TRUE;
532 }
533 
534 /**
535  * camel_name_value_array_remove_named:
536  * @array: a #CamelNameValueArray
537  * @compare_type: a compare type, one of #CamelCompareType
538  * @name: a name to remove
539  * @all_occurrences: whether to remove all occurrences of the @name
540  *
541  * Removes elements of the @array with the given @name.
542  * The @compare_type determines hot to compare the names.
543  * If the @all_occurrences is set to %TRUE, then every elements with the @name
544  * are removed, otherwise only the first occurrence is removed.
545  *
546  * Returns: How many elements had been removed.
547  *
548  * Since: 3.24
549  **/
550 guint
camel_name_value_array_remove_named(CamelNameValueArray * array,CamelCompareType compare_type,const gchar * name,gboolean all_occurrences)551 camel_name_value_array_remove_named (CamelNameValueArray *array,
552 				     CamelCompareType compare_type,
553 				     const gchar *name,
554 				     gboolean all_occurrences)
555 {
556 	guint index, removed = 0;
557 
558 	g_return_val_if_fail (array != NULL, 0);
559 	g_return_val_if_fail (name != NULL, 0);
560 
561 	while (index = camel_name_value_array_find_named (array, compare_type, name), index != (guint) -1) {
562 		if (!camel_name_value_array_remove (array, index))
563 			break;
564 
565 		removed++;
566 
567 		if (!all_occurrences)
568 			break;
569 	}
570 
571 	return removed;
572 }
573 
574 /**
575  * camel_name_value_array_clear:
576  * @array: a #CamelNameValueArray
577  *
578  * Removes all elements of the @array.
579  *
580  * Since: 3.24
581  **/
582 void
camel_name_value_array_clear(CamelNameValueArray * array)583 camel_name_value_array_clear (CamelNameValueArray *array)
584 {
585 	GArray *arr = (GArray *) array;
586 
587 	g_return_if_fail (array != NULL);
588 
589 	g_array_remove_range (arr, 0, arr->len);
590 }
591 
592 /**
593  * camel_name_value_array_equal:
594  * @array_a: (nullable): the first #CamelNameValueArray
595  * @array_b: (nullable): the second #CamelNameValueArray
596  * @compare_type: a compare type, one of #CamelCompareType
597  *
598  * Compares content of the two #CamelNameValueArray and returns whether
599  * they equal. Note this is an expensive operation for large arrays.
600  *
601  * Returns: Whether the two #CamelNameValueArray have the same content.
602  *
603  * Since: 3.24
604  **/
605 gboolean
camel_name_value_array_equal(const CamelNameValueArray * array_a,const CamelNameValueArray * array_b,CamelCompareType compare_type)606 camel_name_value_array_equal (const CamelNameValueArray *array_a,
607 			      const CamelNameValueArray *array_b,
608 			      CamelCompareType compare_type)
609 {
610 	guint ii, len;
611 
612 	if (array_a == array_b)
613 		return TRUE;
614 
615 	if (!array_a || !array_b)
616 		return camel_name_value_array_get_length (array_a) == camel_name_value_array_get_length (array_b);
617 
618 	len = camel_name_value_array_get_length (array_a);
619 	if (len != camel_name_value_array_get_length (array_b))
620 		return FALSE;
621 
622 	for (ii = 0; ii < len; ii++) {
623 		const gchar *value1, *value2;
624 
625 		value1 = camel_name_value_array_get_value (array_a, ii);
626 		value2 = camel_name_value_array_get_named (array_b, compare_type,
627 			camel_name_value_array_get_name (array_a, ii));
628 
629 		if (g_strcmp0 (value1, value2) != 0)
630 			return FALSE;
631 	}
632 
633 	return TRUE;
634 }
635