1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* e-vcard.c
3  *
4  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
5  * Copyright (C) 2013 Collabora Ltd.
6  *
7  * This library is free software: you can redistribute it and/or modify it
8  * under the terms of the GNU Lesser General Public License as published by
9  * the Free Software Foundation.
10  *
11  * This library is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
14  * for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this library. If not, see <http://www.gnu.org/licenses/>.
18  *
19  * Authors: Chris Toshok (toshok@ximian.com)
20  *          Philip Withnall <philip.withnall@collabora.co.uk>
21  */
22 
23 /**
24  * SECTION:e-vcard
25  * @short_description: vCard parsing and interpretation
26  * @stability: Stable
27  * @include: libebook-contacts/libebook-contacts.h
28  *
29  * #EVCard is a low-level representation of a vCard, as specified in RFCs
30  * 2425 and 2426 (for vCard version 3.0); this class only supports versions 2.1
31  * and 3.0 of the vCard standard.
32  *
33  * A vCard is an unordered set of attributes (otherwise known as properties),
34  * each with one or more values. The number of values an attribute has is
35  * determined by its type, and is given in the specification. Each attribute may
36  * also have zero or more named parameters, which provide metadata about its
37  * value.
38  *
39  * For example, the following line from a vCard:
40  * |[
41  * ADR;TYPE=WORK:;;100 Waters Edge;Baytown;LA;30314;United States of America
42  * ]|
43  * is an <code>ADR</code> attribute with 6 values giving the different
44  * components of the postal address. It has one parameter, <code>TYPE</code>,
45  * which specifies that it’s a work address rather than a home address.
46  *
47  * Using the #EVCard API, this data is accessible as follows:
48  * <example>
49  * <title>Accessing a Multi-Valued Attribute</title>
50  * <programlisting>
51  * EVCard *vcard;
52  * EVCardAttribute *attr;
53  * GList *param_values, *values;
54  *
55  * vcard = e_vcard_new_from_string (
56  *    "BEGIN:VCARD\n"
57  *    "VERSION:3.0\n"
58  *    "ADR;TYPE=WORK:;;100 Waters Edge;Baytown;LA;30314;United States of America\n"
59  *    "END:VCARD\n");
60  * attr = e_vcard_get_attribute (vcard, "ADR");
61  *
62  * g_assert_cmpstr (e_vcard_attribute_get_name (attr), ==, "ADR");
63  * g_assert (e_vcard_attribute_is_single_valued (attr) == FALSE);
64  *
65  * param_values = e_vcard_attribute_get_param (attr, "TYPE");
66  * g_assert_cmpuint (g_list_length (param_values), ==, 1);
67  * g_assert_cmpstr (param_values->data, ==, "WORK");
68  *
69  * values = e_vcard_attribute_get_values (attr);
70  * g_assert_cmpuint (g_list_length (values), ==, 6);
71  * g_assert_cmpstr (values->data, ==, "");
72  * g_assert_cmpstr (values->next->data, ==, "100 Waters Edge");
73  * g_assert_cmpstr (values->next->next->data, ==, "Baytown");
74  * /<!-- -->* etc. *<!-- -->/
75  *
76  * g_object_unref (vcard);
77  * </programlisting>
78  * </example>
79  *
80  * If a second <code>ADR</code> attribute was present in the vCard, the above
81  * example would only ever return the first attribute. To access the values of
82  * other attributes of the same type, the entire attribute list must be iterated
83  * over using e_vcard_get_attributes(), then matching on
84  * e_vcard_attribute_get_name().
85  *
86  * vCard attribute values may be encoded in the vCard source, using base-64 or
87  * quoted-printable encoding. Such encoded values are automatically decoded when
88  * parsing the vCard, so the values returned by e_vcard_attribute_get_value()
89  * do not need further decoding. The ‘decoded’ functions,
90  * e_vcard_attribute_get_value_decoded() and
91  * e_vcard_attribute_get_values_decoded() are only relevant when adding
92  * attributes which use pre-encoded values and have their <code>ENCODING</code>
93  * parameter set.
94  *
95  * String comparisons in #EVCard are almost universally case-insensitive.
96  * Attribute names, parameter names and parameter values are all compared
97  * case-insensitively. Only attribute values are case sensitive.
98  *
99  * #EVCard implements lazy parsing of its vCard data, so the first time its
100  * attributes are accessed may take slightly longer than normal to allow for the
101  * vCard to be parsed. This can be tested by calling e_vcard_is_parsed().
102  */
103 
104 /* This file implements the decoding of the v-card format
105  * http://www.imc.org/pdi/vcard-21.txt
106  */
107 
108 #include <stdio.h>
109 #include <string.h>
110 #include <ctype.h>
111 #include "e-vcard.h"
112 
113 #define d(x)
114 
115 #define CRLF "\r\n"
116 
117 static EVCardAttribute *e_vcard_attribute_ref (EVCardAttribute *attr);
118 static void e_vcard_attribute_unref (EVCardAttribute *attr);
119 static EVCardAttributeParam *e_vcard_attribute_param_ref (EVCardAttributeParam *param);
120 static void e_vcard_attribute_param_unref (EVCardAttributeParam *param);
121 
122 /* Encoding used in v-card
123  * Note: v-card spec defines additional 7BIT 8BIT and X- encoding
124  */
125 typedef enum {
126 	EVC_ENCODING_RAW,    /* no encoding */
127 	EVC_ENCODING_BASE64, /* base64 */
128 	EVC_ENCODING_QP      /* quoted-printable */
129 } EVCardEncoding;
130 
131 struct _EVCardPrivate {
132 	GList *attributes;
133 	gchar *vcard;
134 };
135 
136 struct _EVCardAttribute {
137 	gint ref_count;
138 	gchar  *group;
139 	gchar  *name;
140 	GList *params; /* EVCardParam */
141 	GList *values;
142 	GList *decoded_values;
143 	EVCardEncoding encoding;
144 	gboolean encoding_set;
145 };
146 
147 struct _EVCardAttributeParam {
148 	gint ref_count;
149 	gchar     *name;
150 	GList    *values;  /* GList of gchar *'s */
151 };
152 
G_DEFINE_TYPE_WITH_PRIVATE(EVCard,e_vcard,G_TYPE_OBJECT)153 G_DEFINE_TYPE_WITH_PRIVATE (EVCard, e_vcard, G_TYPE_OBJECT)
154 
155 G_DEFINE_BOXED_TYPE (EVCardAttribute, e_vcard_attribute, e_vcard_attribute_ref, e_vcard_attribute_unref)
156 
157 G_DEFINE_BOXED_TYPE (EVCardAttributeParam, e_vcard_attribute_param, e_vcard_attribute_param_ref, e_vcard_attribute_param_unref)
158 
159 static void
160 vcard_finalize (GObject *object)
161 {
162 	EVCardPrivate *priv;
163 
164 	priv = E_VCARD (object)->priv;
165 
166 	/* Directly access priv->attributes and don't call
167 	 * e_vcard_ensure_attributes(), since it is pointless
168 	 * to start vCard parsing that late. */
169 	g_list_free_full (
170 		priv->attributes, (GDestroyNotify) e_vcard_attribute_free);
171 
172 	g_free (priv->vcard);
173 
174 	/* Chain up to parent's finalize() method. */
175 	G_OBJECT_CLASS (e_vcard_parent_class)->finalize (object);
176 }
177 
178 static void
e_vcard_class_init(EVCardClass * class)179 e_vcard_class_init (EVCardClass *class)
180 {
181 	GObjectClass *object_class;
182 
183 	object_class = G_OBJECT_CLASS (class);
184 	object_class->finalize = vcard_finalize;
185 }
186 
187 static void
e_vcard_init(EVCard * evc)188 e_vcard_init (EVCard *evc)
189 {
190 	evc->priv = e_vcard_get_instance_private (evc);
191 }
192 
193 static EVCardAttribute *
e_vcard_attribute_new_take(gchar * attr_group,gchar * attr_name)194 e_vcard_attribute_new_take (gchar *attr_group,
195 			    gchar *attr_name)
196 {
197 	EVCardAttribute *attr;
198 
199 	attr = g_slice_new0 (EVCardAttribute);
200 
201 	if (attr_group && !*attr_group) {
202 		g_free (attr_group);
203 		attr_group = NULL;
204 	}
205 
206 	attr->ref_count = 1;
207 	attr->group = attr_group;
208 	attr->name = attr_name;
209 
210 	return attr;
211 }
212 
213 static gboolean
e_vcard_attribute_is_singlevalue_type(EVCardAttribute * attr)214 e_vcard_attribute_is_singlevalue_type (EVCardAttribute *attr)
215 {
216 	g_return_val_if_fail (attr != NULL, FALSE);
217 
218 	return g_ascii_strcasecmp (attr->name, EVC_KEY) == 0 ||
219 		g_ascii_strcasecmp (attr->name, EVC_LOGO) == 0 ||
220 		g_ascii_strcasecmp (attr->name, EVC_PHOTO) == 0 ||
221 		g_ascii_strcasecmp (attr->name, "SOUND") == 0 ||
222 		g_ascii_strcasecmp (attr->name, "TZ") == 0;
223 }
224 
225 /* Case insensitive version of strstr */
226 static gchar *
strstr_nocase(const gchar * haystack,const gchar * needle)227 strstr_nocase (const gchar *haystack,
228                const gchar *needle)
229 {
230 /* When _GNU_SOURCE is available, use the nonstandard extension of libc */
231 #ifdef _GNU_SOURCE
232 	g_return_val_if_fail (haystack, NULL);
233 	g_return_Val_if_fail (needle, NULL);
234 
235 	return strcasestr (haystack, needle)
236 #else
237 /* Otherwise convert both, haystack and needle to lowercase and use good old strstr */
238 	gchar *l_haystack;
239 	gchar *l_needle;
240 	gchar *pos;
241 
242 	g_return_val_if_fail (haystack, NULL);
243 	g_return_val_if_fail (needle, NULL);
244 
245 	l_haystack = g_ascii_strdown (haystack, -1);
246 	l_needle = g_ascii_strdown (needle, -1);
247 	pos = strstr (l_haystack, l_needle);
248 
249 	/* Get actual position of the needle in the haystack instead of l_haystack or
250 	 * leave it NULL */
251 	if (pos)
252 		pos = (gchar *)(haystack + (pos - l_haystack));
253 
254 	g_free (l_haystack);
255 	g_free (l_needle);
256 
257 	return pos;
258 #endif
259 }
260 
261 /*  Skip newline characters and return the next character.
262  *  This function takes care of folding lines, skipping
263  *  newline characters if found, taking care of equal characters
264  *  and other strange things.
265  */
266 static gchar *
267 skip_newline (gchar *str,
268               gboolean quoted_printable)
269 {
270 	gchar *p;
271 	gchar *next;
272 	gchar *next2;
273 	p = str;
274 
275 	/* -- swallow equal signs at end of line for quoted printable */
276 	/* note: a quoted_printable linefolding is an equal sign followed by
277 	 * one or more newline characters and optional a whitespace */
278 	if (quoted_printable && *p == '=' ) {
279 		next = g_utf8_next_char (p);
280 		if (*next == '\r' || *next == '\n') {
281 			p = g_utf8_next_char (next); /* swallow equal and newline */
282 
283 			if ((*p == '\r' || *p == '\n') && *p != *next ) {
284 				p = g_utf8_next_char (p); /* swallow second newline */
285 
286 				if (*p == ' ' || *p == '\t') {
287 					p = g_utf8_next_char (p); /* swallow whitespace */
288 				}
289 			}
290 
291 		}
292 
293 	/* -- swallow newline and (if present) following whitespaces */
294 	} else if (*p == '\r' || *p == '\n') {
295 
296 		next = g_utf8_next_char (p);
297 		if ((*next == '\n' || *next == '\r') && *p != *next) {
298 
299 			next2 = g_utf8_next_char (next);
300 			if (*next2 == ' ' || *next2 == '\t') {
301 				p = g_utf8_next_char (next2); /* we found a line folding */
302 			}
303 
304 		} else if (*next == ' ' || *next == '\t') {
305 			p = g_utf8_next_char (next);  /* we found a line folding */
306 		}
307 	}
308 
309 	return p;
310 }
311 
312 /* skip forward until we hit the CRLF, or \0 */
313 static void
314 skip_to_next_line (gchar **p)
315 {
316 	gchar *lp = *p;
317 
318 	while (*lp != '\n' && *lp != '\r' && *lp != '\0')
319 		lp = g_utf8_next_char (lp);
320 
321 	/* -- skip over the endline */
322 	while (*lp == '\r' || *lp == '\n') {
323 		lp = g_utf8_next_char (lp);
324 	}
325 
326 	*p = lp;
327 }
328 
329 /* skip forward until we hit a character in @s, CRLF, or \0.  leave *p
330  * pointing at the character that causes us to stop */
331 static void
332 skip_until (gchar **p,
333             const gchar *s)
334 {
335 	gchar *lp;
336 
337 	lp = *p;
338 
339 	while (*lp != '\r' && *lp != '\0') {
340 		gboolean s_matches = FALSE;
341 		const gchar *ls;
342 		for (ls = s; *ls; ls = g_utf8_next_char (ls)) {
343 			if (g_utf8_get_char (ls) == g_utf8_get_char (lp)) {
344 				s_matches = TRUE;
345 				break;
346 			}
347 		}
348 
349 		if (s_matches)
350 			break;
351 		lp = g_utf8_next_char (lp);
352 	}
353 
354 	*p = lp;
355 }
356 
357 static void
358 read_attribute_value (EVCardAttribute *attr,
359                       gchar **p,
360                       gboolean quoted_printable,
361                       const gchar *charset)
362 {
363 	gchar *lp = *p;
364 	const gchar *chunk_start = NULL;
365 	GString *str;
366 
367 	#define WRITE_CHUNK() G_STMT_START { \
368 		if (chunk_start) { \
369 			g_string_append_len (str, chunk_start, lp - chunk_start); \
370 			chunk_start = NULL; \
371 		} } G_STMT_END
372 
373 	/* read in the value */
374 	str = g_string_sized_new (16);
375 	for (lp = skip_newline ( *p, quoted_printable);
376 	     *lp != '\n' && *lp != '\r' && *lp != '\0';
377 	     lp = skip_newline ( lp, quoted_printable ) ) {
378 
379 		if (*lp == '=' && quoted_printable) {
380 			gunichar a, b;
381 
382 			WRITE_CHUNK ();
383 
384 			/* it's for the '=' */
385 			lp++;
386 			lp = skip_newline (lp, quoted_printable);
387 
388 			a = g_utf8_get_char (lp);
389 			lp = g_utf8_next_char (lp);
390 			if (a == '\0')
391 				break;
392 
393 			lp = skip_newline (lp, quoted_printable);
394 
395 			b = g_utf8_get_char (lp);
396 			if (b == '\0')
397 				break;
398 			lp = g_utf8_next_char (lp);
399 
400 			if (g_unichar_isxdigit (a) && g_unichar_isxdigit (b)) {
401 				gchar c;
402 
403 				gint a_bin = g_unichar_xdigit_value (a);
404 				gint b_bin = g_unichar_xdigit_value (b);
405 
406 				c = (a_bin << 4) | b_bin;
407 
408 				g_string_append_c (str, c); /* add decoded byte (this is not a unicode yet) */
409 			} else {
410 				g_string_append_c (str, '=');
411 				g_string_insert_unichar (str, -1, a);
412 				g_string_insert_unichar (str, -1, b);
413 			}
414 		} else if (*lp == '\\') {
415 			WRITE_CHUNK ();
416 
417 			/* convert back to the non-escaped version of
418 			 * the characters */
419 			lp = g_utf8_next_char (lp);
420 			if (*lp == '\0') {
421 				g_string_append_c (str, '\\');
422 				break;
423 			}
424 
425 			/* beware, there might be a line break due to folding,
426 			 * need next real character
427 			 */
428 			lp = skip_newline (lp, quoted_printable);
429 
430 			switch (*lp) {
431 			case 'n': g_string_append_c (str, '\n'); break;
432 			case 'N': g_string_append_c (str, '\n'); break;
433 			case 'r': g_string_append_c (str, '\r'); break;
434 			case 'R': g_string_append_c (str, '\r'); break;
435 			case ';': g_string_append_c (str, ';'); break;
436 			case ',': g_string_append_c (str, ','); break;
437 			case '\\': g_string_append_c (str, '\\'); break;
438 			default:
439 				g_warning ("invalid escape, passing it through");
440 				g_string_append_c (str, '\\');
441 				chunk_start = lp;
442 				break;
443 			}
444 			lp = g_utf8_next_char (lp);
445 		}
446 		else if ((*lp == ';' && !e_vcard_attribute_is_singlevalue_type (attr)) ||
447 			 (*lp == ',' && !g_ascii_strcasecmp (attr->name, "CATEGORIES"))) {
448 			WRITE_CHUNK ();
449 			if (charset) {
450 				gchar *tmp;
451 
452 				tmp = g_convert (str->str, str->len, "UTF-8", charset, NULL, NULL, NULL);
453 				if (tmp) {
454 					g_string_assign (str, tmp);
455 					g_free (tmp);
456 				}
457 			}
458 
459 			e_vcard_attribute_add_value (attr, str->str);
460 			g_string_set_size (str, 0);
461 			lp = g_utf8_next_char (lp);
462 		}
463 		else {
464 			if (!chunk_start)
465 				chunk_start = lp;
466 
467 			lp = g_utf8_next_char (lp);
468 		}
469 
470 		if (*lp == '\n' || *lp == '\r')
471 			WRITE_CHUNK ();
472 	}
473 
474 	WRITE_CHUNK ();
475 
476 	#undef WRITE_CHUNK
477 
478 	if (str) {
479 		if (charset) {
480 			gchar *tmp;
481 
482 			tmp = g_convert (str->str, str->len, "UTF-8", charset, NULL, NULL, NULL);
483 			if (tmp) {
484 				g_string_assign (str, tmp);
485 				g_free (tmp);
486 			}
487 		}
488 
489 		e_vcard_attribute_add_value (attr, str->str);
490 		g_string_free (str, TRUE);
491 	}
492 
493 	skip_to_next_line ( &lp );
494 
495 	*p = lp;
496 }
497 
498 static void
499 read_attribute_params (EVCardAttribute *attr,
500                        gchar **p,
501                        gboolean *quoted_printable,
502                        gchar **charset)
503 {
504 	gchar *lp;
505 	const gchar *chunk_start = NULL;
506 	GString *str;
507 	EVCardAttributeParam *param = NULL;
508 	gboolean in_quote = FALSE;
509 
510 	#define WRITE_CHUNK() G_STMT_START { \
511 		if (chunk_start) { \
512 			g_string_append_len (str, chunk_start, lp - chunk_start); \
513 			chunk_start = NULL; \
514 		} } G_STMT_END
515 
516 	str = g_string_sized_new (16);
517 	for (lp = skip_newline ( *p, *quoted_printable);
518 	     *lp != '\n' && *lp != '\r' && *lp != '\0';
519 	     lp = skip_newline ( lp, *quoted_printable ) ) {
520 		gunichar uc;
521 
522 		if (*lp == '"') {
523 			WRITE_CHUNK ();
524 
525 			in_quote = !in_quote;
526 			lp = g_utf8_next_char (lp);
527 		} else  if (uc = g_utf8_get_char (lp), in_quote || *lp == '-' || *lp == '_' || g_unichar_isalnum (uc)) {
528 			WRITE_CHUNK ();
529 
530 			g_string_insert_unichar (str, -1, uc);
531 			lp = g_utf8_next_char (lp);
532 		}
533 		/* accumulate until we hit the '=' or ';'.  If we hit
534 		 * a '=' the string contains the parameter name.  if
535 		 * we hit a ';' the string contains the parameter
536 		 * value and the name is either ENCODING (if value ==
537 		 * QUOTED-PRINTABLE) or TYPE (in any other case.)
538 		 */
539 		else if (*lp == '=') {
540 			WRITE_CHUNK ();
541 
542 			if (str->len > 0) {
543 				param = e_vcard_attribute_param_new (str->str);
544 				g_string_set_size (str, 0);
545 				lp = g_utf8_next_char (lp);
546 			}
547 			else {
548 				skip_until (&lp, ":;");
549 				if (*lp == ';') {
550 					lp = g_utf8_next_char (lp);
551 
552 				} else if (*lp == ':') {
553 					/* do nothing */
554 
555 				} else {
556 					skip_to_next_line ( &lp );
557 					break;
558 				}
559 			}
560 		}
561 		else if (*lp == ';' || *lp == ':' || *lp == ',') {
562 			gboolean colon = (*lp == ':');
563 			gboolean comma = (*lp == ',');
564 
565 			WRITE_CHUNK ();
566 
567 			if (param) {
568 				if (str->len > 0) {
569 					e_vcard_attribute_param_add_value (param, str->str);
570 					g_string_set_size (str, 0);
571 					if (!colon)
572 						lp = g_utf8_next_char (lp);
573 				}
574 				else {
575 					/* we've got a parameter of the form:
576 					 * PARAM=(.*,)?[:;]
577 					 * so what we do depends on if there are already values
578 					 * for the parameter.  If there are, we just finish
579 					 * this parameter and skip past the offending character
580 					 * (unless it's the ':'). If there aren't values, we free
581 					 * the parameter then skip past the character.
582 					 */
583 					if (!param->values) {
584 						e_vcard_attribute_param_free (param);
585 						param = NULL;
586 						if (!colon)
587 							lp = g_utf8_next_char (lp);
588 					} else if (!colon) {
589 						lp = g_utf8_next_char (lp);
590 					}
591 				}
592 
593 				if (param
594 				    && !g_ascii_strcasecmp (param->name, "encoding")
595 				    && !g_ascii_strcasecmp (param->values->data, "quoted-printable")) {
596 					*quoted_printable = TRUE;
597 					e_vcard_attribute_param_free (param);
598 					param = NULL;
599 				} else if (param
600 				    && !g_ascii_strcasecmp (param->name, "charset")
601 				    && g_ascii_strcasecmp (param->values->data, "utf-8") != 0) {
602 					g_free (*charset);
603 					*charset = g_strdup (param->values->data);
604 					e_vcard_attribute_param_free (param);
605 					param = NULL;
606 				}
607 			}
608 			else {
609 				if (str->len > 0) {
610 					const gchar *param_name;
611 					if (!g_ascii_strcasecmp (str->str,
612 								 "quoted-printable")) {
613 						param_name = "ENCODING";
614 						*quoted_printable = TRUE;
615 					}
616 					/* apple's broken addressbook app outputs naked BASE64
617 					 * parameters, which aren't even vcard 3.0 compliant. */
618 					else if (!g_ascii_strcasecmp (str->str,
619 								      "base64")) {
620 						param_name = "ENCODING";
621 						g_string_assign (str, "b");
622 					}
623 					else {
624 						param_name = "TYPE";
625 					}
626 
627 					param = e_vcard_attribute_param_new (param_name);
628 					e_vcard_attribute_param_add_value (param, str->str);
629 					g_string_set_size (str, 0);
630 					if (!colon)
631 						lp = g_utf8_next_char (lp);
632 				}
633 				else {
634 					/* we've got an attribute with a truly empty
635 					 * attribute parameter.  So it's of the form:
636 					 *
637 					 * ATTR;[PARAM=value;]*;[PARAM=value;]*:
638 					 *
639 					 * (note the extra ';')
640 					 *
641 					 * the only thing to do here is, well.. nothing.
642 					 * we skip over the character if it's not a colon,
643 					 * and the rest is handled for us: We'll either
644 					 * continue through the loop again if we hit a ';',
645 					 * or we'll break out correct below if it was a ':' */
646 					if (!colon)
647 						lp = g_utf8_next_char (lp);
648 				}
649 			}
650 			if (param && !comma) {
651 				e_vcard_attribute_add_param (attr, param);
652 				param = NULL;
653 			}
654 			if (colon)
655 				break;
656 		} else if (param) {
657 			/* reading param value, which is SAFE-CHAR, aka
658 			 * any character except CTLs, DQUOTE, ";", ":", "," */
659 			if (!chunk_start)
660 				chunk_start = lp;
661 
662 			lp = g_utf8_next_char (lp);
663 		} else {
664 			g_warning ("invalid character (%c/0x%02x) found in parameter spec (%s)", *lp, *lp, lp);
665 			chunk_start = NULL;
666 			g_string_set_size (str, 0);
667 			/*			skip_until (&lp, ":;"); */
668 
669 			skip_to_next_line ( &lp );
670 		}
671 
672 		if (*lp == '\n' || *lp == '\r')
673 			WRITE_CHUNK ();
674 	}
675 
676 	#undef WRITE_CHUNK
677 
678 	if (str)
679 		g_string_free (str, TRUE);
680 
681 	*p = lp;
682 }
683 
684 /* reads an entire attribute from the input buffer, leaving p pointing
685  * at the start of the next line (past the \r\n) */
686 static EVCardAttribute *
687 read_attribute (gchar **p)
688 {
689 	gchar *attr_group = NULL;
690 	gchar *attr_name = NULL;
691 	EVCardAttribute *attr = NULL;
692 	GString *str;
693 	gchar *lp;
694 	gboolean is_qp = FALSE;
695 	gchar *charset = NULL;
696 
697 	/* first read in the group/name */
698 	str = g_string_sized_new (16);
699 	for (lp = skip_newline ( *p, is_qp);
700 	     *lp != '\n' && *lp != '\r' && *lp != '\0';
701 	     lp = skip_newline ( lp, is_qp ) ) {
702 		gunichar uc;
703 
704 		if (*lp == ':' || *lp == ';') {
705 			if (str->len != 0) {
706 				/* we've got a name, break out to the value/attribute parsing */
707 				attr_name = g_string_free (str, FALSE);
708 				break;
709 			}
710 			else {
711 				/* a line of the form:
712 				 * (group.)?[:;]
713 				 *
714 				 * since we don't have an attribute
715 				 * name, skip to the end of the line
716 				 * and try again.
717 				 */
718 				g_string_free (str, TRUE);
719 				*p = lp;
720 				skip_to_next_line (p);
721 				goto lose;
722 			}
723 		}
724 		else if (*lp == '.') {
725 			if (attr_group) {
726 				g_warning (
727 					"extra `.' in attribute specification.  ignoring extra group `%s'",
728 					str->str);
729 				g_string_set_size (str, 0);
730 			}
731 			if (str->len != 0) {
732 				attr_group = g_string_free (str, FALSE);
733 				str = g_string_sized_new (16);
734 			}
735 		}
736 		else if (uc = g_utf8_get_char (lp), *lp == '-' || *lp == '_' || g_unichar_isalnum (uc)) {
737 			g_string_insert_unichar (str, -1, uc);
738 		}
739 		else {
740 			g_warning ("invalid character found in attribute group/name");
741 			g_string_free (str, TRUE);
742 			*p = lp;
743 			skip_to_next_line (p);
744 			goto lose;
745 		}
746 
747 		lp = g_utf8_next_char (lp);
748 	}
749 
750 	if (!attr_name) {
751 		skip_to_next_line (p);
752 		goto lose;
753 	}
754 
755 	/* This consumes (takes) both strings */
756 	attr = e_vcard_attribute_new_take (attr_group, attr_name);
757 
758 	if (*lp == ';') {
759 		/* skip past the ';' */
760 		lp = g_utf8_next_char (lp);
761 		read_attribute_params (attr, &lp, &is_qp, &charset);
762 		if (is_qp)
763 			attr->encoding = EVC_ENCODING_RAW;
764 	}
765 	if (*lp == ':') {
766 		/* skip past the ':' */
767 		lp = g_utf8_next_char (lp);
768 		read_attribute_value (attr, &lp, is_qp, charset);
769 	}
770 
771 	g_free (charset);
772 
773 	*p = lp;
774 
775 	if (!attr->values)
776 		goto lose;
777 
778 	return attr;
779  lose:
780 	if (attr)
781 		e_vcard_attribute_free (attr);
782 	return NULL;
783 }
784 
785 /* Stolen from glib/glib/gconvert.c */
786 static gchar *
787 make_valid_utf8 (const gchar *name)
788 {
789 	GString *string;
790 	const gchar *remainder, *invalid;
791 	gint remaining_bytes, valid_bytes;
792 
793 	string = NULL;
794 	remainder = name;
795 	remaining_bytes = strlen (name);
796 
797 	while (remaining_bytes != 0) {
798 		if (g_utf8_validate (remainder, remaining_bytes, &invalid))
799 			break;
800 	      valid_bytes = invalid - remainder;
801 
802 		if (string == NULL)
803 			string = g_string_sized_new (remaining_bytes);
804 
805 		g_string_append_len (string, remainder, valid_bytes);
806 		/* append U+FFFD REPLACEMENT CHARACTER */
807 		g_string_append (string, "\357\277\275");
808 
809 		remaining_bytes -= valid_bytes + 1;
810 		remainder = invalid + 1;
811 	}
812 
813 	if (string == NULL)
814 		return g_strdup (name);
815 
816 	g_string_append (string, remainder);
817 
818 	g_return_val_if_fail (g_utf8_validate (string->str, -1, NULL), g_string_free (string, TRUE));
819 
820 	return g_string_free (string, FALSE);
821 }
822 
823 /* we try to be as forgiving as we possibly can here - this isn't a
824  * validator.  Almost nothing is considered a fatal error.  We always
825  * try to return *something*.
826  */
827 static void
828 parse (EVCard *evc,
829        const gchar *str,
830        gboolean ignore_uid)
831 {
832 	gchar *buf;
833 	gchar *p;
834 	EVCardAttribute *attr;
835 
836 	buf = make_valid_utf8 (str);
837 
838 	d (printf ("BEFORE FOLDING:\n"));
839 	d (printf (str));
840 	d (printf ("\n\nAFTER FOLDING:\n"));
841 	d (printf (buf));
842 
843 	p = buf;
844 
845 	attr = read_attribute (&p);
846 	if (!attr || attr->group || g_ascii_strcasecmp (attr->name, "begin")) {
847 		g_warning ("vcard began without a BEGIN:VCARD\n");
848 	}
849 	if (attr && !g_ascii_strcasecmp (attr->name, "begin")) {
850 		e_vcard_attribute_free (attr);
851 		attr = NULL;
852 	} else if (attr) {
853 		if ((!ignore_uid || g_ascii_strcasecmp (attr->name, EVC_UID) != 0) &&
854 		    g_ascii_strcasecmp (attr->name, "end") != 0)
855 			e_vcard_add_attribute (evc, attr);
856 	}
857 	while (*p) {
858 		EVCardAttribute *next_attr = read_attribute (&p);
859 
860 		if (next_attr) {
861 			attr = next_attr;
862 
863 			if (g_ascii_strcasecmp (next_attr->name, "end") == 0)
864 				break;
865 
866 			if (ignore_uid && g_ascii_strcasecmp (attr->name, EVC_UID) == 0) {
867 				e_vcard_attribute_free (attr);
868 				attr = NULL;
869 				continue;
870 			}
871 
872 			e_vcard_add_attribute (evc, next_attr);
873 		}
874 	}
875 
876 	if (!attr || attr->group || g_ascii_strcasecmp (attr->name, "end")) {
877 		g_warning ("vcard ended without END:VCARD\n");
878 	}
879 
880 	if (attr && !g_ascii_strcasecmp (attr->name, "end"))
881 		e_vcard_attribute_free (attr);
882 
883 	g_free (buf);
884 
885 	evc->priv->attributes = g_list_reverse (evc->priv->attributes);
886 }
887 
888 static GList *
889 e_vcard_ensure_attributes (EVCard *evc)
890 {
891 	if (evc->priv->vcard) {
892 		gboolean have_uid = (evc->priv->attributes != NULL);
893 		gchar *vcs = evc->priv->vcard;
894 
895 		/* detach vCard to avoid loops */
896 		evc->priv->vcard = NULL;
897 
898 		/* Parse the vCard */
899 		parse (evc, vcs, have_uid);
900 		g_free (vcs);
901 	}
902 
903 	return evc->priv->attributes;
904 }
905 
906 static gchar *
907 e_vcard_escape_semicolons (const gchar *s)
908 {
909 	GString *str;
910 	const gchar *p;
911 
912 	if (s)
913 		str = g_string_sized_new (strlen (s));
914 	else
915 		str = g_string_new ("");
916 
917 	for (p = s; p && *p; p++) {
918 		if (*p == ';')
919 			g_string_append_c (str, '\\');
920 
921 		g_string_append_c (str, *p);
922 	}
923 
924 	return g_string_free (str, FALSE);
925 }
926 
927 /**
928  * e_vcard_escape_string:
929  * @s: the string to escape
930  *
931  * Escapes a string according to RFC2426, section 5.
932  *
933  * Returns: (transfer full): A newly allocated, escaped string.
934  **/
935 gchar *
936 e_vcard_escape_string (const gchar *s)
937 {
938 	GString *str;
939 	const gchar *p;
940 
941 	if (s)
942 		str = g_string_sized_new (strlen (s));
943 	else
944 		str = g_string_new ("");
945 
946 	/* Escape a string as described in RFC2426, section 5 */
947 	for (p = s; p && *p; p++) {
948 		switch (*p) {
949 		case '\n':
950 			g_string_append (str, "\\n");
951 			break;
952 		case '\r':
953 			if (*(p + 1) == '\n')
954 				p++;
955 			g_string_append (str, "\\n");
956 			break;
957 		case ';':
958 			g_string_append (str, "\\;");
959 			break;
960 		case ',':
961 			g_string_append (str, "\\,");
962 			break;
963 		case '\\':
964 			g_string_append (str, "\\\\");
965 			break;
966 		default:
967 			g_string_append_c (str, *p);
968 			break;
969 		}
970 	}
971 
972 	return g_string_free (str, FALSE);
973 }
974 
975 /**
976  * e_vcard_unescape_string:
977  * @s: the string to unescape
978  *
979  * Unescapes a string according to RFC2426, section 5.
980  *
981  * Returns: (transfer full): A newly allocated, unescaped string.
982  **/
983 gchar *
984 e_vcard_unescape_string (const gchar *s)
985 {
986 	GString *str;
987 	const gchar *p;
988 
989 	g_return_val_if_fail (s != NULL, NULL);
990 
991 	str = g_string_sized_new (strlen (s));
992 
993 	/* Unescape a string as described in RFC2426, section 5 */
994 	for (p = s; *p; p++) {
995 		if (*p == '\\') {
996 			p++;
997 			if (*p == '\0') {
998 				g_string_append_c (str, '\\');
999 				break;
1000 			}
1001 			switch (*p) {
1002 			case 'n':  g_string_append_c (str, '\n'); break;
1003 			case 'r':  g_string_append_c (str, '\r'); break;
1004 			case ';':  g_string_append_c (str, ';'); break;
1005 			case ',':  g_string_append_c (str, ','); break;
1006 			case '\\': g_string_append_c (str, '\\'); break;
1007 			default:
1008 				g_warning ("invalid escape, passing it through");
1009 				g_string_append_c (str, '\\');
1010 				g_string_append_c (str, *p);
1011 				break;
1012 			}
1013 		} else {
1014 			g_string_append_c (str, *p);
1015 		}
1016 	}
1017 
1018 	return g_string_free (str, FALSE);
1019 }
1020 
1021 /**
1022  * e_vcard_construct:
1023  * @evc: an existing #EVCard
1024  * @str: a vCard string
1025  *
1026  * Constructs the existing #EVCard, @evc, setting its vCard data to @str.
1027  *
1028  * This modifies @evc.
1029  */
1030 void
1031 e_vcard_construct (EVCard *evc,
1032                    const gchar *str)
1033 {
1034 	e_vcard_construct_with_uid (evc, str, NULL);
1035 }
1036 
1037 /**
1038  * e_vcard_construct_with_uid:
1039  * @evc: an existing #EVCard
1040  * @str: a vCard string
1041  * @uid: (nullable): a unique ID string
1042  *
1043  * Constructs the existing #EVCard, @evc, setting its vCard data to @str, and
1044  * adding a new UID attribute with the value given in @uid (if @uid is
1045  * non-%NULL).
1046  *
1047  * This modifies @evc.
1048  *
1049  * Since: 3.4
1050  **/
1051 void
1052 e_vcard_construct_with_uid (EVCard *evc,
1053                             const gchar *str,
1054                             const gchar *uid)
1055 {
1056 	e_vcard_construct_full (evc, str, -1, uid);
1057 }
1058 
1059 /**
1060  * e_vcard_construct_full:
1061  * @evc: an existing #EVCard
1062  * @str: a vCard string
1063  * @len: length of @str, or -1 if @str is %NULL terminated
1064  * @uid: (nullable): a unique ID string
1065  *
1066  * Similar to e_vcard_construct_with_uid(), but can also
1067  * be used with an @str that is not %NULL terminated.
1068  *
1069  * Since: 3.12
1070  **/
1071 void
1072 e_vcard_construct_full (EVCard *evc,
1073                         const gchar *str,
1074                         gssize len,
1075                         const gchar *uid)
1076 {
1077 	g_return_if_fail (E_IS_VCARD (evc));
1078 	g_return_if_fail (str != NULL);
1079 	g_return_if_fail (evc->priv->vcard == NULL);
1080 	g_return_if_fail (evc->priv->attributes == NULL);
1081 
1082 	/* Lazy construction */
1083 	if (*str) {
1084 
1085 		if (len < 0)
1086 			evc->priv->vcard = g_strdup (str);
1087 		else
1088 			evc->priv->vcard = g_strndup (str, len);
1089 	}
1090 
1091 	/* Add UID attribute */
1092 	if (uid) {
1093 		EVCardAttribute *attr;
1094 
1095 		attr = e_vcard_attribute_new (NULL, EVC_UID);
1096 		e_vcard_attribute_add_value (attr, uid);
1097 
1098 		evc->priv->attributes = g_list_prepend (evc->priv->attributes, attr);
1099 	}
1100 }
1101 
1102 /**
1103  * e_vcard_new:
1104  *
1105  * Creates a new, blank #EVCard.
1106  *
1107  * Returns: (transfer full): A new, blank #EVCard.
1108  **/
1109 EVCard *
1110 e_vcard_new (void)
1111 {
1112 	return e_vcard_new_from_string ("");
1113 }
1114 
1115 /**
1116  * e_vcard_new_from_string:
1117  * @str: a string representation of the vcard to create
1118  *
1119  * Creates a new #EVCard from the passed-in string
1120  * representation.
1121  *
1122  * Returns: (transfer full): A new #EVCard.
1123  **/
1124 EVCard *
1125 e_vcard_new_from_string (const gchar *str)
1126 {
1127 	EVCard *evc;
1128 
1129 	g_return_val_if_fail (str != NULL, NULL);
1130 
1131 	evc = g_object_new (E_TYPE_VCARD, NULL);
1132 
1133 	e_vcard_construct (evc, str);
1134 
1135 	return evc;
1136 }
1137 
1138 static gchar *
1139 e_vcard_qp_encode (const gchar *txt,
1140                    gboolean can_wrap)
1141 {
1142 	const gchar *p = txt;
1143 	GString *escaped = g_string_sized_new (strlen (txt));
1144 	gint count = 0;
1145 
1146 	while (*p != '\0') {
1147 		if ((*p >= 33 && *p <= 60) || (*p >= 62 && *p <= 126)) {
1148 			if (can_wrap && count == 75) {
1149 				g_string_append (escaped, "=" CRLF " ");
1150 				count = 1; /* 1 for space needed for folding */
1151 			}
1152 
1153 			g_string_append_c (escaped, *p++);
1154 			count++;
1155 
1156 			continue;
1157 		}
1158 
1159 		if (count >= 73 && can_wrap) {
1160 			g_string_append (escaped, "=" CRLF " ");
1161 			count = 1; /* 1 for space needed for folding */
1162 		}
1163 
1164 		g_string_append_printf (escaped, "=%2.2X", (guchar) *p++);
1165 		count += 3;
1166 	}
1167 
1168 	return g_string_free (escaped, FALSE);
1169 }
1170 
1171 static gchar *
1172 e_vcard_qp_decode (const gchar *txt)
1173 {
1174 	const gchar *inptr;
1175 	gchar *decoded, *outptr;
1176 
1177 	if (!txt)
1178 		return NULL;
1179 
1180 	decoded = g_malloc (sizeof (gchar) * strlen (txt) + 1);
1181 
1182 	outptr = decoded;
1183 
1184 	for (inptr = txt; *inptr; inptr++) {
1185 		gchar c = *inptr;
1186 
1187 		if (c == '=' && (inptr[1] == '\r' || inptr[1] == '\n')) {
1188 			/* soft line-break */
1189 			if (inptr[2] == '\n')
1190 				inptr++;
1191 			inptr++;
1192 			continue;
1193 		}
1194 
1195 		if (c == '=' && inptr[1] && inptr[2]) {
1196 			guchar a = toupper (inptr[1]), b = toupper (inptr[2]);
1197 			if (isxdigit (a) && isxdigit (b)) {
1198 				*outptr++ = (((a >= 'A' ? a - 'A' + 10 : a - '0') & 0x0f) << 4)
1199 					   | ((b >= 'A' ? b - 'A' + 10 : b - '0') & 0x0f);
1200 			} else {
1201 				*outptr++ = '=';
1202 				*outptr++ = inptr[1];
1203 				*outptr++ = inptr[2];
1204 			}
1205 
1206 			inptr += 2;
1207 		} else {
1208 			*outptr++ = *inptr;
1209 		}
1210 	}
1211 
1212 	*outptr = '\0';
1213 
1214 	return decoded;
1215 }
1216 
1217 static GHashTable *
1218 generate_dict_validator (const gchar *words)
1219 {
1220 	GHashTable *dict = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
1221 	gchar **list = g_strsplit (words, ",", 0);
1222 	gint i;
1223 
1224 	for (i = 0; list[i]; i++)
1225 		g_hash_table_insert (dict, list[i], NULL);
1226 
1227 	g_free (list);
1228 
1229 	return dict;
1230 }
1231 
1232 static gchar *
1233 e_vcard_to_string_vcard_21 (EVCard *evc)
1234 {
1235 	GList *l, *v;
1236 	GString *str = g_string_new ("");
1237 	GHashTable *evc_prop = generate_dict_validator (E_VCARD_21_VALID_PROPERTIES);
1238 	GHashTable *evc_params = generate_dict_validator (E_VCARD_21_VALID_PARAMETERS);
1239 
1240 	g_string_append (str, "BEGIN:VCARD" CRLF);
1241 	g_string_append (str, "VERSION:2.1" CRLF);
1242 
1243 	for (l = e_vcard_ensure_attributes (evc); l; l = l->next) {
1244 		GList *list;
1245 		EVCardAttribute *attr = l->data;
1246 		GString *attr_str;
1247 		gboolean empty, encode;
1248 		gchar *upper_name;
1249 
1250 		if (g_ascii_strcasecmp (attr->name, "VERSION") == 0)
1251 			continue;
1252 
1253 		upper_name = g_ascii_strup (attr->name, -1);
1254 		/* Checking whether current property (attribute) is valid for vCard 2.1 */
1255 		if (!g_hash_table_lookup_extended (evc_prop, upper_name, NULL, NULL)) {
1256 			g_free (upper_name);
1257 
1258 			continue;
1259 		}
1260 		g_free (upper_name);
1261 
1262 		empty = TRUE; /* Empty fields should be omitted -- some headsets may choke on it */
1263 		encode = FALSE; /* Generally only new line MUST be encoded (Quoted Printable) */
1264 
1265 		for (v = attr->values; v; v = v->next) {
1266 			gchar *value = v->data;
1267 
1268 			if (value && *value)
1269 				empty = FALSE;
1270 			else
1271 				continue;
1272 
1273 			if (strstr (value, "\n") != NULL) {
1274 				encode = TRUE;
1275 				break;
1276 			}
1277 		}
1278 
1279 		if (empty)
1280 			continue;
1281 
1282 		attr_str = g_string_sized_new (strlen (attr->name) + (attr->group ? strlen (attr->group) + 1 : 0));
1283 
1284 		/* From vCard 2.1 spec page 27, 28
1285 		 *
1286 		 * contentline  = [group "."] name *(";" param) ":" value CRLF
1287 		 */
1288 
1289 		if (attr->group) {
1290 			g_string_append (attr_str, attr->group);
1291 			g_string_append_c (attr_str, '.');
1292 		}
1293 		g_string_append (attr_str, attr->name);
1294 
1295 		/* handle the parameters */
1296 		for (list = attr->params; list; list = list->next) {
1297 			EVCardAttributeParam *param = list->data;
1298 
1299 			/* Page 28
1300 			 *
1301 			 * param        = param-name "=" param-value
1302 			 */
1303 
1304 			upper_name = g_ascii_strup (param->name, -1);
1305 			/* Checking whether current parameter is valid for vCard 2.1 */
1306 			if (!g_hash_table_lookup_extended (evc_params, upper_name, NULL, NULL)) {
1307 				g_free (upper_name);
1308 
1309 				continue;
1310 			}
1311 			g_free (upper_name);
1312 
1313 			g_string_append_c (attr_str, ';');
1314 			g_string_append (attr_str, param->name);
1315 			if (!param->values)
1316 				continue;
1317 
1318 			for (v = param->values; v; v = v->next) {
1319 				gchar *value = v->data;
1320 				gchar *escaped_value = e_vcard_escape_semicolons (value);
1321 
1322 				g_string_append_printf (attr_str, "=%s", escaped_value);
1323 
1324 				if (v->next)
1325 					g_string_append_printf (attr_str, ";%s", param->name);
1326 
1327 				g_free (escaped_value);
1328 			}
1329 		}
1330 
1331 		if (encode)
1332 			g_string_append (attr_str, ";ENCODING=QUOTED-PRINTABLE");
1333 
1334 		g_string_append_c (attr_str, ':');
1335 
1336 		for (v = attr->values; v; v = v->next) {
1337 			gchar *value = v->data;
1338 
1339 			if (encode) {
1340 				gchar *escaped_value;
1341 
1342 				escaped_value = e_vcard_qp_encode (value, TRUE);
1343 				g_string_append (attr_str, escaped_value);
1344 
1345 				g_free (escaped_value);
1346 			} else
1347 				g_string_append (attr_str, value);
1348 
1349 			if (v->next)
1350 				g_string_append_c (attr_str, ';');
1351 		}
1352 
1353 		g_string_append (attr_str, CRLF);
1354 
1355 		g_string_append (str, attr_str->str);
1356 
1357 		g_string_free (attr_str, TRUE);
1358 	}
1359 
1360 	g_string_append (str, "END:VCARD");
1361 
1362 	g_hash_table_destroy (evc_params);
1363 	g_hash_table_destroy (evc_prop);
1364 
1365 	return g_string_free (str, FALSE);
1366 }
1367 
1368 static gchar *
1369 e_vcard_to_string_vcard_30 (EVCard *evc)
1370 {
1371 	GList *l;
1372 	GList *v;
1373 
1374 	GString *str = g_string_new ("");
1375 
1376 	g_string_append (str, "BEGIN:VCARD" CRLF);
1377 
1378 	/* we hardcode the version (since we're outputting to a
1379 	 * specific version) and ignore any version attributes the
1380 	 * vcard might contain */
1381 	g_string_append (str, "VERSION:3.0" CRLF);
1382 
1383 	for (l = e_vcard_ensure_attributes (evc); l; l = l->next) {
1384 		GList *list;
1385 		EVCardAttribute *attr = l->data;
1386 		GString *attr_str;
1387 		glong len;
1388 		EVCardAttributeParam *quoted_printable_param = NULL;
1389 
1390 		if (!g_ascii_strcasecmp (attr->name, "VERSION"))
1391 			continue;
1392 
1393 		attr_str = g_string_sized_new (strlen (attr->name) + (attr->group ? strlen (attr->group) + 1 : 0));
1394 
1395 		/* From rfc2425, 5.8.2
1396 		 *
1397 		 * contentline  = [group "."] name *(";" param) ":" value CRLF
1398 		 */
1399 
1400 		if (attr->group) {
1401 			g_string_append (attr_str, attr->group);
1402 			g_string_append_c (attr_str, '.');
1403 		}
1404 		g_string_append (attr_str, attr->name);
1405 
1406 		/* handle the parameters */
1407 		for (list = attr->params; list; list = list->next) {
1408 			EVCardAttributeParam *param = list->data;
1409 
1410 			/* quoted-printable encoding was eliminated in 3.0,
1411 			 * thus decode the value before saving and remove the param later */
1412 			if (!quoted_printable_param &&
1413 			    param->values && param->values->data && !param->values->next &&
1414 			    g_ascii_strcasecmp (param->name, "ENCODING") == 0 &&
1415 			    g_ascii_strcasecmp (param->values->data, "quoted-printable") == 0) {
1416 				quoted_printable_param = param;
1417 				/* do not store it */
1418 				continue;
1419 			}
1420 
1421 			/* 5.8.2:
1422 			 * param        = param-name "=" param-value *("," param-value)
1423 			 */
1424 			g_string_append_c (attr_str, ';');
1425 			g_string_append (attr_str, param->name);
1426 			if (param->values) {
1427 				g_string_append_c (attr_str, '=');
1428 
1429 				for (v = param->values; v; v = v->next) {
1430 					gchar *value = v->data;
1431 					gchar *pval = value;
1432 					gboolean quotes = FALSE;
1433 					while (pval && *pval) {
1434 						if (!g_unichar_isalnum (g_utf8_get_char (pval))) {
1435 							quotes = TRUE;
1436 							break;
1437 						}
1438 						pval = g_utf8_next_char (pval);
1439 					}
1440 
1441 					if (quotes) {
1442 						gint i;
1443 
1444 						g_string_append_c (attr_str, '"');
1445 
1446 						for (i = 0; value[i]; i++) {
1447 							/* skip quotes in quoted string; it is not allowed */
1448 							if (value[i] == '\"')
1449 								continue;
1450 
1451 							g_string_append_c (attr_str, value[i]);
1452 						}
1453 
1454 						g_string_append_c (attr_str, '"');
1455 					} else
1456 						g_string_append (attr_str, value);
1457 
1458 					if (v->next)
1459 						g_string_append_c (attr_str, ',');
1460 				}
1461 			}
1462 		}
1463 
1464 		g_string_append_c (attr_str, ':');
1465 
1466 		for (v = attr->values; v; v = v->next) {
1467 			gchar *value = v->data;
1468 			gchar *escaped_value = NULL;
1469 
1470 			/* values are in quoted-printable encoding, but this cannot be used in vCard 3.0,
1471 			 * thus it needs to be converted first */
1472 			if (quoted_printable_param) {
1473 				gchar *qp_decoded;
1474 
1475 				qp_decoded = e_vcard_qp_decode (value);
1476 
1477 				/* replace the actual value with the decoded */
1478 				g_free (value);
1479 				value = qp_decoded;
1480 				v->data = value;
1481 			}
1482 
1483 			escaped_value = e_vcard_escape_string (value);
1484 
1485 			g_string_append (attr_str, escaped_value);
1486 			if (v->next) {
1487 				/* XXX toshok - i hate you, rfc 2426.
1488 				 * why doesn't CATEGORIES use a; like
1489 				 * a normal list attribute? */
1490 				if (!g_ascii_strcasecmp (attr->name, "CATEGORIES"))
1491 					g_string_append_c (attr_str, ',');
1492 				else
1493 					g_string_append_c (attr_str, ';');
1494 			}
1495 
1496 			g_free (escaped_value);
1497 		}
1498 
1499 		/* 5.8.2:
1500 		 * When generating a content line, lines longer than 75
1501 		 * characters SHOULD be folded
1502 		 */
1503 		len = g_utf8_strlen (attr_str->str, -1);
1504 		if (len > 75) {
1505 			GString *fold_str = g_string_sized_new (attr_str->len + len / 74 * 3);
1506 			gchar *pos1 = attr_str->str;
1507 			gchar *pos2 = pos1;
1508 			pos2 = g_utf8_offset_to_pointer (pos2, 75);
1509 			len -= 75;
1510 
1511 			while (1) {
1512 				g_string_append_len (fold_str, pos1, pos2 - pos1);
1513 				g_string_append (fold_str, CRLF " ");
1514 				pos1 = pos2;
1515 				if (len <= 74)
1516 					break;
1517 				pos2 = g_utf8_offset_to_pointer (pos2, 74);
1518 				len -= 74;
1519 			}
1520 			g_string_append (fold_str, pos1);
1521 			g_string_free (attr_str, TRUE);
1522 			attr_str = fold_str;
1523 		}
1524 		g_string_append (attr_str, CRLF);
1525 
1526 		g_string_append (str, attr_str->str);
1527 		g_string_free (attr_str, TRUE);
1528 
1529 		/* remove the encoding parameter, to not decode multiple times */
1530 		if (quoted_printable_param)
1531 			e_vcard_attribute_remove_param (attr, quoted_printable_param->name);
1532 	}
1533 
1534 	g_string_append (str, "END:VCARD");
1535 
1536 	return g_string_free (str, FALSE);
1537 }
1538 
1539 /**
1540  * e_vcard_to_string:
1541  * @evc: the #EVCard to export
1542  * @format: the format to export to
1543  *
1544  * Exports @evc to a string representation, specified
1545  * by the @format argument.
1546  *
1547  * Returns: (transfer full): A newly allocated string representing the vcard.
1548  **/
1549 gchar *
1550 e_vcard_to_string (EVCard *evc,
1551                    EVCardFormat format)
1552 {
1553 	g_return_val_if_fail (E_IS_VCARD (evc), NULL);
1554 
1555 	switch (format) {
1556 	case EVC_FORMAT_VCARD_21:
1557 		if (evc->priv->vcard && evc->priv->attributes == NULL &&
1558 		    strstr_nocase (evc->priv->vcard, CRLF "VERSION:2.1" CRLF))
1559 			return g_strdup (evc->priv->vcard);
1560 
1561 		return e_vcard_to_string_vcard_21 (evc);
1562 	case EVC_FORMAT_VCARD_30:
1563 		if (evc->priv->vcard && evc->priv->attributes == NULL &&
1564 		    strstr_nocase (evc->priv->vcard, CRLF "VERSION:3.0" CRLF))
1565 			return g_strdup (evc->priv->vcard);
1566 
1567 		return e_vcard_to_string_vcard_30 (evc);
1568 	default:
1569 		g_warning ("invalid format specifier passed to e_vcard_to_string");
1570 		return g_strdup ("");
1571 	}
1572 }
1573 
1574 /**
1575  * e_vcard_dump_structure:
1576  * @evc: the #EVCard to dump
1577  *
1578  * Prints a dump of @evc's structure to stdout. Used for
1579  * debugging.
1580  **/
1581 void
1582 e_vcard_dump_structure (EVCard *evc)
1583 {
1584 	GList *a;
1585 	GList *v;
1586 	gint i;
1587 
1588 	g_return_if_fail (E_IS_VCARD (evc));
1589 
1590 	printf ("vCard\n");
1591 	for (a = e_vcard_ensure_attributes (evc); a; a = a->next) {
1592 		GList *p;
1593 		EVCardAttribute *attr = a->data;
1594 		printf ("+-- %s\n", attr->name);
1595 		if (attr->params) {
1596 			printf ("    +- params=\n");
1597 
1598 			for (p = attr->params, i = 0; p; p = p->next, i++) {
1599 				EVCardAttributeParam *param = p->data;
1600 				printf ("    |   [%d] = %s", i,param->name);
1601 				printf ("(");
1602 				for (v = param->values; v; v = v->next) {
1603 					gchar *value = e_vcard_escape_string ((gchar *) v->data);
1604 					printf ("%s", value);
1605 					if (v->next)
1606 						printf (",");
1607 					g_free (value);
1608 				}
1609 
1610 				printf (")\n");
1611 			}
1612 		}
1613 		printf ("    +- values=\n");
1614 		for (v = attr->values, i = 0; v; v = v->next, i++) {
1615 			printf ("        [%d] = `%s'\n", i, (gchar *) v->data);
1616 		}
1617 	}
1618 }
1619 
1620 /**
1621  * e_vcard_attribute_new:
1622  * @attr_group: (nullable): a group name
1623  * @attr_name: an attribute name
1624  *
1625  * Creates a new #EVCardAttribute with the specified group and
1626  * attribute names. The @attr_group may be %NULL or the empty string if no
1627  * group is needed.
1628  *
1629  * Returns: (transfer full): A new #EVCardAttribute.
1630  **/
1631 EVCardAttribute *
1632 e_vcard_attribute_new (const gchar *attr_group,
1633                        const gchar *attr_name)
1634 {
1635 	return e_vcard_attribute_new_take ((attr_group && *attr_group) ? g_strdup (attr_group) : NULL, g_strdup (attr_name));
1636 }
1637 
1638 /**
1639  * e_vcard_attribute_free:
1640  * @attr: (transfer full): attribute to free
1641  *
1642  * Frees an attribute, its values and its parameters.
1643  **/
1644 void
1645 e_vcard_attribute_free (EVCardAttribute *attr)
1646 {
1647 	g_return_if_fail (attr != NULL);
1648 
1649 	e_vcard_attribute_unref (attr);
1650 }
1651 
1652 static EVCardAttribute *
1653 e_vcard_attribute_ref (EVCardAttribute *attr)
1654 {
1655 	g_return_val_if_fail (attr != NULL, NULL);
1656 
1657 	g_atomic_int_inc (&attr->ref_count);
1658 
1659 	return attr;
1660 }
1661 
1662 static void
1663 e_vcard_attribute_unref (EVCardAttribute *attr)
1664 {
1665 	g_return_if_fail (attr != NULL);
1666 
1667 	if (g_atomic_int_dec_and_test (&attr->ref_count)) {
1668 		g_free (attr->group);
1669 		g_free (attr->name);
1670 
1671 		e_vcard_attribute_remove_values (attr);
1672 
1673 		e_vcard_attribute_remove_params (attr);
1674 
1675 		g_slice_free (EVCardAttribute, attr);
1676 	}
1677 }
1678 
1679 /**
1680  * e_vcard_attribute_copy:
1681  * @attr: attribute to copy
1682  *
1683  * Makes a copy of @attr.
1684  *
1685  * Returns: (transfer full): A new #EVCardAttribute identical to @attr.
1686  **/
1687 EVCardAttribute *
1688 e_vcard_attribute_copy (EVCardAttribute *attr)
1689 {
1690 	EVCardAttribute *a;
1691 	GList *p;
1692 
1693 	g_return_val_if_fail (attr != NULL, NULL);
1694 
1695 	a = e_vcard_attribute_new (attr->group, attr->name);
1696 
1697 	for (p = attr->values; p; p = p->next)
1698 		e_vcard_attribute_add_value (a, p->data);
1699 
1700 	for (p = attr->params; p; p = p->next)
1701 		e_vcard_attribute_add_param (a, e_vcard_attribute_param_copy (p->data));
1702 
1703 	return a;
1704 }
1705 
1706 /**
1707  * e_vcard_remove_attributes:
1708  * @evc: vcard object
1709  * @attr_group: (nullable): group name of attributes to be removed
1710  * @attr_name: name of the arributes to be removed
1711  *
1712  * Removes all the attributes with group name and attribute name equal to the
1713  * passed in values. If @attr_group is %NULL or an empty string,
1714  * it removes all the attributes with passed in name irrespective of
1715  * their group names.
1716  **/
1717 void
1718 e_vcard_remove_attributes (EVCard *evc,
1719                            const gchar *attr_group,
1720                            const gchar *attr_name)
1721 {
1722 	GList *attr;
1723 
1724 	g_return_if_fail (E_IS_VCARD (evc));
1725 	g_return_if_fail (attr_name != NULL);
1726 
1727 	attr = e_vcard_ensure_attributes (evc);
1728 	while (attr) {
1729 		GList *next_attr;
1730 		EVCardAttribute *a = attr->data;
1731 
1732 		next_attr = attr->next;
1733 
1734 		if (((!attr_group || *attr_group == '\0') ||
1735 		     (attr_group && !g_ascii_strcasecmp (attr_group, a->group))) &&
1736 		    ((!a->name) || !g_ascii_strcasecmp (attr_name, a->name))) {
1737 
1738 			/* matches, remove/delete the attribute */
1739 			evc->priv->attributes = g_list_delete_link (evc->priv->attributes, attr);
1740 
1741 			e_vcard_attribute_free (a);
1742 		}
1743 
1744 		attr = next_attr;
1745 	}
1746 }
1747 
1748 /**
1749  * e_vcard_remove_attribute:
1750  * @evc: an #EVCard
1751  * @attr: (transfer full): an #EVCardAttribute to remove
1752  *
1753  * Removes @attr from @evc and frees it. This takes ownership of @attr.
1754  **/
1755 void
1756 e_vcard_remove_attribute (EVCard *evc,
1757                           EVCardAttribute *attr)
1758 {
1759 	g_return_if_fail (E_IS_VCARD (evc));
1760 	g_return_if_fail (attr != NULL);
1761 
1762 	/* No need to call e_vcard_ensure_attributes() here. It has
1763 	 * already been called if this is a valid call and attr is among
1764 	 * our attributes. */
1765 	evc->priv->attributes = g_list_remove (evc->priv->attributes, attr);
1766 	e_vcard_attribute_free (attr);
1767 }
1768 
1769 /**
1770  * e_vcard_append_attribute:
1771  * @evc: an #EVCard
1772  * @attr: (transfer full): an #EVCardAttribute to append
1773  *
1774  * Appends @attr to @evc to the end of a list of attributes. This takes
1775  * ownership of @attr.
1776  *
1777  * Since: 2.32
1778  **/
1779 void
1780 e_vcard_append_attribute (EVCard *evc,
1781                           EVCardAttribute *attr)
1782 {
1783 	g_return_if_fail (E_IS_VCARD (evc));
1784 	g_return_if_fail (attr != NULL);
1785 
1786 	/* Handle UID special case:
1787 	 * No need to parse the vcard to append an UID attribute */
1788 	if (evc->priv->vcard != NULL && attr->name != NULL &&
1789 	    g_ascii_strcasecmp (attr->name, EVC_UID) == 0) {
1790 		evc->priv->attributes = g_list_append (evc->priv->attributes, attr);
1791 	} else {
1792 		evc->priv->attributes = g_list_append (e_vcard_ensure_attributes (evc), attr);
1793 	}
1794 }
1795 
1796 /**
1797  * e_vcard_append_attribute_with_value:
1798  * @evcard: an #EVCard
1799  * @attr: (transfer full): an #EVCardAttribute to append
1800  * @value: a value to assign to the attribute
1801  *
1802  * Appends @attr to @evcard, setting it to @value. This takes ownership of
1803  * @attr.
1804  *
1805  * This is a convenience wrapper around e_vcard_attribute_add_value() and
1806  * e_vcard_append_attribute().
1807  *
1808  * Since: 2.32
1809  **/
1810 void
1811 e_vcard_append_attribute_with_value (EVCard *evcard,
1812                                      EVCardAttribute *attr,
1813                                      const gchar *value)
1814 {
1815 	g_return_if_fail (E_IS_VCARD (evcard));
1816 	g_return_if_fail (attr != NULL);
1817 
1818 	e_vcard_attribute_add_value (attr, value);
1819 
1820 	e_vcard_append_attribute (evcard, attr);
1821 }
1822 
1823 /**
1824  * e_vcard_append_attribute_with_values:
1825  * @evcard: an @EVCard
1826  * @attr: (transfer full): an #EVCardAttribute to append
1827  * @...: a %NULL-terminated list of values to assign to the attribute
1828  *
1829  * Appends @attr to @evcard, assigning the list of values to it. This takes
1830  * ownership of @attr.
1831  *
1832  * This is a convenience wrapper around e_vcard_attribute_add_value() and
1833  * e_vcard_append_attribute().
1834  *
1835  * Since: 2.32
1836  **/
1837 void
1838 e_vcard_append_attribute_with_values (EVCard *evcard,
1839                                       EVCardAttribute *attr,
1840                                       ...)
1841 {
1842 	va_list ap;
1843 	gchar *v;
1844 
1845 	g_return_if_fail (E_IS_VCARD (evcard));
1846 	g_return_if_fail (attr != NULL);
1847 
1848 	va_start (ap, attr);
1849 
1850 	while ((v = va_arg (ap, gchar *))) {
1851 		e_vcard_attribute_add_value (attr, v);
1852 	}
1853 
1854 	va_end (ap);
1855 
1856 	e_vcard_append_attribute (evcard, attr);
1857 }
1858 
1859 /**
1860  * e_vcard_add_attribute:
1861  * @evc: an #EVCard
1862  * @attr: (transfer full): an #EVCardAttribute to add
1863  *
1864  * Prepends @attr to @evc. This takes ownership of @attr.
1865  **/
1866 void
1867 e_vcard_add_attribute (EVCard *evc,
1868                        EVCardAttribute *attr)
1869 {
1870 	g_return_if_fail (E_IS_VCARD (evc));
1871 	g_return_if_fail (attr != NULL);
1872 
1873 	/* Handle UID special case:
1874 	 * No need to parse the vcard to append an UID attribute */
1875 	if (evc->priv->vcard != NULL && attr->name != NULL &&
1876 	    g_ascii_strcasecmp (attr->name, EVC_UID) == 0) {
1877 		evc->priv->attributes = g_list_prepend (evc->priv->attributes, attr);
1878 	} else {
1879 		evc->priv->attributes = g_list_prepend (e_vcard_ensure_attributes (evc), attr);
1880 	}
1881 }
1882 
1883 /**
1884  * e_vcard_add_attribute_with_value:
1885  * @evcard: an #EVCard
1886  * @attr: (transfer full): an #EVCardAttribute to add
1887  * @value: a value to assign to the attribute
1888  *
1889  * Prepends @attr to @evcard, setting it to @value. This takes ownership of
1890  * @attr.
1891  *
1892  * This is a convenience wrapper around e_vcard_attribute_add_value() and
1893  * e_vcard_add_attribute().
1894  **/
1895 void
1896 e_vcard_add_attribute_with_value (EVCard *evcard,
1897                                   EVCardAttribute *attr,
1898                                   const gchar *value)
1899 {
1900 	g_return_if_fail (E_IS_VCARD (evcard));
1901 	g_return_if_fail (attr != NULL);
1902 
1903 	e_vcard_attribute_add_value (attr, value);
1904 
1905 	e_vcard_add_attribute (evcard, attr);
1906 }
1907 
1908 /**
1909  * e_vcard_add_attribute_with_values:
1910  * @evcard: an @EVCard
1911  * @attr: (transfer full): an #EVCardAttribute to add
1912  * @...: a %NULL-terminated list of values to assign to the attribute
1913  *
1914  * Prepends @attr to @evcard, assigning the list of values to it. This takes
1915  * ownership of @attr.
1916  *
1917  * This is a convenience wrapper around e_vcard_attribute_add_value() and
1918  * e_vcard_add_attribute().
1919  **/
1920 void
1921 e_vcard_add_attribute_with_values (EVCard *evcard,
1922                                    EVCardAttribute *attr,
1923                                    ...)
1924 {
1925 	va_list ap;
1926 	gchar *v;
1927 
1928 	g_return_if_fail (E_IS_VCARD (evcard));
1929 	g_return_if_fail (attr != NULL);
1930 
1931 	va_start (ap, attr);
1932 
1933 	while ((v = va_arg (ap, gchar *))) {
1934 		e_vcard_attribute_add_value (attr, v);
1935 	}
1936 
1937 	va_end (ap);
1938 
1939 	e_vcard_add_attribute (evcard, attr);
1940 }
1941 
1942 /**
1943  * e_vcard_attribute_add_value:
1944  * @attr: an #EVCardAttribute
1945  * @value: a string value
1946  *
1947  * Appends @value to @attr's list of values.
1948  **/
1949 void
1950 e_vcard_attribute_add_value (EVCardAttribute *attr,
1951                              const gchar *value)
1952 {
1953 	g_return_if_fail (attr != NULL);
1954 
1955 	attr->values = g_list_append (attr->values, g_strdup (value));
1956 }
1957 
1958 /**
1959  * e_vcard_attribute_add_value_decoded:
1960  * @attr: an #EVCardAttribute
1961  * @value: an encoded value
1962  * @len: the length of the encoded value, in bytes
1963  *
1964  * Encodes @value according to the encoding used for @attr, and appends it to
1965  * @attr's list of values.
1966  *
1967  * This should only be used if the #EVCardAttribute has a non-raw encoding (i.e.
1968  * if it’s encoded in base-64 or quoted-printable encoding).
1969  **/
1970 void
1971 e_vcard_attribute_add_value_decoded (EVCardAttribute *attr,
1972                                      const gchar *value,
1973                                      gint len)
1974 {
1975 	g_return_if_fail (attr != NULL);
1976 
1977 	switch (attr->encoding) {
1978 	case EVC_ENCODING_RAW:
1979 		g_warning ("can't add_value_decoded with an attribute using RAW encoding.  you must set the ENCODING parameter first");
1980 		break;
1981 	case EVC_ENCODING_BASE64: {
1982 		gchar *b64_data = g_base64_encode ((guchar *) value, len);
1983 		GString *decoded = g_string_new_len (value, len);
1984 
1985 		/* make sure the decoded list is up to date */
1986 		e_vcard_attribute_get_values_decoded (attr);
1987 
1988 		d (printf ("base64 encoded value: %s\n", b64_data));
1989 		d (printf ("original length: %d\n", len));
1990 
1991 		attr->values = g_list_append (attr->values, b64_data);
1992 		attr->decoded_values = g_list_append (attr->decoded_values, decoded);
1993 		break;
1994 	}
1995 	case EVC_ENCODING_QP: {
1996 		GString *decoded = g_string_new_len (value, len);
1997 		gchar *qp_data = e_vcard_qp_encode (decoded->str, FALSE);
1998 
1999 		/* make sure the decoded list is up to date */
2000 		e_vcard_attribute_get_values_decoded (attr);
2001 
2002 		d (printf ("qp encoded value: %s\n", qp_data));
2003 		d (printf ("original length: %d\n", len));
2004 
2005 		attr->values = g_list_append (attr->values, qp_data);
2006 		attr->decoded_values = g_list_append (attr->decoded_values, decoded);
2007 		break;
2008 	}
2009 	}
2010 }
2011 
2012 /**
2013  * e_vcard_attribute_add_values:
2014  * @attr: an #EVCardAttribute
2015  * @...: a %NULL-terminated list of strings
2016  *
2017  * Appends a list of values to @attr.
2018  **/
2019 void
2020 e_vcard_attribute_add_values (EVCardAttribute *attr,
2021                               ...)
2022 {
2023 	va_list ap;
2024 	gchar *v;
2025 
2026 	g_return_if_fail (attr != NULL);
2027 
2028 	va_start (ap, attr);
2029 
2030 	while ((v = va_arg (ap, gchar *))) {
2031 		e_vcard_attribute_add_value (attr, v);
2032 	}
2033 
2034 	va_end (ap);
2035 }
2036 
2037 static void
2038 free_gstring (GString *str)
2039 {
2040 	g_string_free (str, TRUE);
2041 }
2042 
2043 /**
2044  * e_vcard_attribute_remove_values:
2045  * @attr: an #EVCardAttribute
2046  *
2047  * Removes and frees all values from @attr.
2048  **/
2049 void
2050 e_vcard_attribute_remove_values (EVCardAttribute *attr)
2051 {
2052 	g_return_if_fail (attr != NULL);
2053 
2054 	g_list_foreach (attr->values, (GFunc) g_free, NULL);
2055 	g_list_free (attr->values);
2056 	attr->values = NULL;
2057 
2058 	g_list_foreach (attr->decoded_values, (GFunc) free_gstring, NULL);
2059 	g_list_free (attr->decoded_values);
2060 	attr->decoded_values = NULL;
2061 }
2062 
2063 /**
2064  * e_vcard_attribute_remove_value:
2065  * @attr: an #EVCardAttribute
2066  * @s: a value to remove
2067  *
2068  * Removes value @s from the value list in @attr. The value @s is not freed.
2069  **/
2070 void
2071 e_vcard_attribute_remove_value (EVCardAttribute *attr,
2072                                 const gchar *s)
2073 {
2074 	GList *l;
2075 
2076 	g_return_if_fail (attr != NULL);
2077 	g_return_if_fail (s != NULL);
2078 
2079 	l = g_list_find_custom (attr->values, s, (GCompareFunc) strcmp);
2080 	if (l == NULL) {
2081 		return;
2082 	}
2083 
2084 	attr->values = g_list_delete_link (attr->values, l);
2085 }
2086 
2087 /**
2088  * e_vcard_attribute_remove_param:
2089  * @attr: an #EVCardAttribute
2090  * @param_name: a parameter name
2091  *
2092  * Removes and frees parameter @param_name from the attribute @attr. Parameter
2093  * names are guaranteed to be unique, so @attr is guaranteed to have no
2094  * parameters named @param_name after this function returns.
2095  *
2096  * Since: 1.12
2097  */
2098 void
2099 e_vcard_attribute_remove_param (EVCardAttribute *attr,
2100                                 const gchar *param_name)
2101 {
2102 	GList *l;
2103 	EVCardAttributeParam *param;
2104 
2105 	g_return_if_fail (attr != NULL);
2106 	g_return_if_fail (param_name != NULL);
2107 
2108 	for (l = attr->params; l; l = l->next) {
2109 		param = l->data;
2110 		if (g_ascii_strcasecmp (e_vcard_attribute_param_get_name (param),
2111 					param_name) == 0) {
2112 			if (g_ascii_strcasecmp (param_name, EVC_ENCODING) == 0) {
2113 				attr->encoding_set = FALSE;
2114 				attr->encoding = EVC_ENCODING_RAW;
2115 			}
2116 
2117 			attr->params = g_list_delete_link (attr->params, l);
2118 			e_vcard_attribute_param_free (param);
2119 			break;
2120 		}
2121 	}
2122 }
2123 
2124 /**
2125  * e_vcard_attribute_remove_params:
2126  * @attr: an #EVCardAttribute
2127  *
2128  * Removes and frees all parameters from @attr.
2129  *
2130  * This also resets the #EVCardAttribute's encoding back to raw.
2131  **/
2132 void
2133 e_vcard_attribute_remove_params (EVCardAttribute *attr)
2134 {
2135 	g_return_if_fail (attr != NULL);
2136 
2137 	g_list_foreach (attr->params, (GFunc) e_vcard_attribute_param_free, NULL);
2138 	g_list_free (attr->params);
2139 	attr->params = NULL;
2140 
2141 	/* also remove the cached encoding on this attribute */
2142 	attr->encoding_set = FALSE;
2143 	attr->encoding = EVC_ENCODING_RAW;
2144 }
2145 
2146 /**
2147  * e_vcard_attribute_param_new:
2148  * @name: the name of the new parameter
2149  *
2150  * Creates a new parameter named @name.
2151  *
2152  * Returns: (transfer full): A new #EVCardAttributeParam.
2153  **/
2154 EVCardAttributeParam *
2155 e_vcard_attribute_param_new (const gchar *name)
2156 {
2157 	EVCardAttributeParam *param = g_slice_new (EVCardAttributeParam);
2158 
2159 	param->ref_count = 1;
2160 	param->values = NULL;
2161 	param->name = g_strdup (name);
2162 
2163 	return param;
2164 }
2165 
2166 /**
2167  * e_vcard_attribute_param_free:
2168  * @param: (transfer full): an #EVCardAttributeParam
2169  *
2170  * Frees @param and its values.
2171  **/
2172 void
2173 e_vcard_attribute_param_free (EVCardAttributeParam *param)
2174 {
2175 	g_return_if_fail (param != NULL);
2176 
2177 	e_vcard_attribute_param_unref (param);
2178 }
2179 
2180 static EVCardAttributeParam *
2181 e_vcard_attribute_param_ref (EVCardAttributeParam *param)
2182 {
2183 	g_return_val_if_fail (param != NULL, NULL);
2184 
2185 	g_atomic_int_inc (&param->ref_count);
2186 
2187 	return param;
2188 }
2189 
2190 static void
2191 e_vcard_attribute_param_unref (EVCardAttributeParam *param)
2192 {
2193 	g_return_if_fail (param != NULL);
2194 
2195 	if (g_atomic_int_dec_and_test (&param->ref_count)) {
2196 		g_free (param->name);
2197 
2198 		e_vcard_attribute_param_remove_values (param);
2199 
2200 		g_slice_free (EVCardAttributeParam, param);
2201 	}
2202 }
2203 
2204 /**
2205  * e_vcard_attribute_param_copy:
2206  * @param: an #EVCardAttributeParam
2207  *
2208  * Makes a copy of @param and all its values.
2209  *
2210  * Returns: (transfer full): a new #EVCardAttributeParam identical to @param.
2211  **/
2212 EVCardAttributeParam *
2213 e_vcard_attribute_param_copy (EVCardAttributeParam *param)
2214 {
2215 	EVCardAttributeParam *p;
2216 	GList *l;
2217 
2218 	g_return_val_if_fail (param != NULL, NULL);
2219 
2220 	p = e_vcard_attribute_param_new (e_vcard_attribute_param_get_name (param));
2221 
2222 	for (l = param->values; l; l = l->next) {
2223 		e_vcard_attribute_param_add_value (p, l->data);
2224 	}
2225 
2226 	return p;
2227 }
2228 
2229 /**
2230  * e_vcard_attribute_add_param:
2231  * @attr: an #EVCardAttribute
2232  * @param: (transfer full): an #EVCardAttributeParam to add
2233  *
2234  * Prepends @param to @attr's list of parameters. This takes ownership of
2235  * @param (and all its values).
2236  *
2237  * Duplicate parameters have their values merged, so that all parameter names
2238  * in @attr are unique. Values are also merged so that uniqueness is preserved.
2239  **/
2240 void
2241 e_vcard_attribute_add_param (EVCardAttribute *attr,
2242                              EVCardAttributeParam *param)
2243 {
2244 	gboolean contains;
2245 	GList *params, *p;
2246 	const gchar *par_name;
2247 
2248 	g_return_if_fail (attr != NULL);
2249 	g_return_if_fail (param != NULL);
2250 
2251 	contains = FALSE;
2252 	params = attr->params;
2253 	par_name = param->name;
2254 
2255 	for (p = params; p; p = p->next) {
2256 		EVCardAttributeParam *param2 = p->data;
2257 		if (g_ascii_strcasecmp (param2->name, par_name) == 0) {
2258 			/* param2 has same name as our new param;
2259 			 * better merge them than have more parameters
2260 			 * with same name within one attribute.
2261 			*/
2262 			GList *vals,*v;
2263 
2264 			vals = param->values;
2265 
2266 			for (v = vals; v; v = v->next) {
2267 				const gchar *my_value;
2268 				GList *vals2,*v2;
2269 
2270 				my_value = (const gchar *) v->data;
2271 				vals2 = param2->values;
2272 
2273 				for (v2 = vals2; v2; v2 = v2->next) {
2274 					if (g_ascii_strcasecmp ((const gchar *) v2->data, my_value) == 0) {
2275 						break;
2276 					}
2277 				}
2278 
2279 				if (!v2) {
2280 					/* we did loop through all values and none of them was my_value */
2281 					e_vcard_attribute_param_add_value (param2, my_value);
2282 				}
2283 			}
2284 
2285 			contains = TRUE;
2286 			break;
2287 		}
2288 	}
2289 
2290 	if (!contains) {
2291 		attr->params = g_list_prepend (attr->params, param);
2292 	}
2293 
2294 	/* we handle our special encoding stuff here */
2295 
2296 	if (!g_ascii_strcasecmp (param->name, EVC_ENCODING)) {
2297 		if (attr->encoding_set) {
2298 			g_warning ("ENCODING specified twice");
2299 			if (contains) {
2300 				e_vcard_attribute_param_free (param);
2301 			}
2302 			return;
2303 		}
2304 
2305 		if (param->values && param->values->data) {
2306 			if (!g_ascii_strcasecmp ((gchar *) param->values->data, "b") ||
2307 			    !g_ascii_strcasecmp ((gchar *) param->values->data, "BASE64"))
2308 				attr->encoding = EVC_ENCODING_BASE64;
2309 			else if (!g_ascii_strcasecmp ((gchar *) param->values->data, EVC_QUOTEDPRINTABLE))
2310 				attr->encoding = EVC_ENCODING_QP;
2311 			else {
2312 				g_warning (
2313 					"Unknown value `%s' for ENCODING parameter.  values will be treated as raw",
2314 					(gchar *) param->values->data);
2315 			}
2316 
2317 			attr->encoding_set = TRUE;
2318 		}
2319 		else {
2320 			g_warning ("ENCODING parameter added with no value");
2321 		}
2322 	}
2323 
2324 	if (contains) {
2325 		e_vcard_attribute_param_free (param);
2326 	}
2327 }
2328 
2329 /**
2330  * e_vcard_attribute_param_add_value:
2331  * @param: an #EVCardAttributeParam
2332  * @value: a string value to add
2333  *
2334  * Appends @value to @param's list of values.
2335  **/
2336 void
2337 e_vcard_attribute_param_add_value (EVCardAttributeParam *param,
2338                                    const gchar *value)
2339 {
2340 	g_return_if_fail (param != NULL);
2341 
2342 	param->values = g_list_append (param->values, g_strdup (value));
2343 }
2344 
2345 /**
2346  * e_vcard_attribute_param_add_values:
2347  * @param: an #EVCardAttributeParam
2348  * @...: a %NULL-terminated list of strings
2349  *
2350  * Appends a list of values to @param.
2351  **/
2352 void
2353 e_vcard_attribute_param_add_values (EVCardAttributeParam *param,
2354                                     ...)
2355 {
2356 	va_list ap;
2357 	gchar *v;
2358 
2359 	g_return_if_fail (param != NULL);
2360 
2361 	va_start (ap, param);
2362 
2363 	while ((v = va_arg (ap, gchar *))) {
2364 		e_vcard_attribute_param_add_value (param, v);
2365 	}
2366 
2367 	va_end (ap);
2368 }
2369 
2370 /**
2371  * e_vcard_attribute_add_param_with_value:
2372  * @attr: an #EVCardAttribute
2373  * @param: (transfer full): an #EVCardAttributeParam
2374  * @value: a string value
2375  *
2376  * Appends @value to @param, then prepends @param to @attr. This takes ownership
2377  * of @param, but not of @value.
2378  *
2379  * This is a convenience method for e_vcard_attribute_param_add_value() and
2380  * e_vcard_attribute_add_param().
2381  **/
2382 void
2383 e_vcard_attribute_add_param_with_value (EVCardAttribute *attr,
2384                                         EVCardAttributeParam *param,
2385                                         const gchar *value)
2386 {
2387 	g_return_if_fail (attr != NULL);
2388 	g_return_if_fail (param != NULL);
2389 
2390 	e_vcard_attribute_param_add_value (param, value);
2391 
2392 	e_vcard_attribute_add_param (attr, param);
2393 }
2394 
2395 /**
2396  * e_vcard_attribute_add_param_with_values:
2397  * @attr: an #EVCardAttribute
2398  * @param: (transfer full): an #EVCardAttributeParam
2399  * @...: a %NULL-terminated list of strings
2400  *
2401  * Appends the list of values to @param, then prepends @param to @attr. This
2402  * takes ownership of @param, but not of the list of values.
2403  *
2404  * This is a convenience method for e_vcard_attribute_param_add_value() and
2405  * e_vcard_attribute_add_param().
2406  **/
2407 void
2408 e_vcard_attribute_add_param_with_values (EVCardAttribute *attr,
2409                                          EVCardAttributeParam *param, ...)
2410 {
2411 	va_list ap;
2412 	gchar *v;
2413 
2414 	g_return_if_fail (attr != NULL);
2415 	g_return_if_fail (param != NULL);
2416 
2417 	va_start (ap, param);
2418 
2419 	while ((v = va_arg (ap, gchar *))) {
2420 		e_vcard_attribute_param_add_value (param, v);
2421 	}
2422 
2423 	va_end (ap);
2424 
2425 	e_vcard_attribute_add_param (attr, param);
2426 }
2427 
2428 /**
2429  * e_vcard_attribute_param_remove_values:
2430  * @param: an #EVCardAttributeParam
2431  *
2432  * Removes and frees all values from @param.
2433  **/
2434 void
2435 e_vcard_attribute_param_remove_values (EVCardAttributeParam *param)
2436 {
2437 	g_return_if_fail (param != NULL);
2438 
2439 	g_list_foreach (param->values, (GFunc) g_free, NULL);
2440 	g_list_free (param->values);
2441 	param->values = NULL;
2442 }
2443 
2444 /**
2445  * e_vcard_attribute_remove_param_value:
2446  * @attr: an #EVCardAttribute
2447  * @param_name: a parameter name
2448  * @s: a value
2449  *
2450  * Removes the value @s from the parameter @param_name on the attribute @attr.
2451  * If @s was the only value for parameter @param_name, that parameter is removed
2452  * entirely from @attr and freed.
2453  **/
2454 void
2455 e_vcard_attribute_remove_param_value (EVCardAttribute *attr,
2456                                       const gchar *param_name,
2457                                       const gchar *s)
2458 {
2459 	GList *l, *params;
2460 	EVCardAttributeParam *param;
2461 
2462 	g_return_if_fail (attr != NULL);
2463 	g_return_if_fail (param_name != NULL);
2464 	g_return_if_fail (s != NULL);
2465 
2466 	params = e_vcard_attribute_get_params (attr);
2467 
2468 	for (l = params; l; l = l->next) {
2469 		param = l->data;
2470 		if (g_ascii_strcasecmp (e_vcard_attribute_param_get_name (param), param_name) == 0) {
2471 			l = g_list_find_custom (param->values, s, (GCompareFunc) g_ascii_strcasecmp);
2472 			if (l == NULL) {
2473 				return;
2474 			}
2475 
2476 			g_free (l->data);
2477 			param->values = g_list_delete_link (param->values, l);
2478 
2479 			if (param->values == NULL) {
2480 				e_vcard_attribute_param_free (param);
2481 				attr->params = g_list_remove (attr->params, param);
2482 			}
2483 			break;
2484 		}
2485 	}
2486 	return;
2487 }
2488 
2489 /**
2490  * e_vcard_get_attributes:
2491  * @evcard: an #EVCard
2492  *
2493  * Gets the list of all attributes from @evcard. The list and its
2494  * contents are owned by @evcard, and must not be freed.
2495  *
2496  * Returns: (transfer none) (element-type EVCardAttribute): A list of attributes
2497  * of type #EVCardAttribute.
2498  **/
2499 GList *
2500 e_vcard_get_attributes (EVCard *evcard)
2501 {
2502 	g_return_val_if_fail (E_IS_VCARD (evcard), NULL);
2503 
2504 	return e_vcard_ensure_attributes (evcard);
2505 }
2506 
2507 /**
2508  * e_vcard_get_attribute:
2509  * @evc: an #EVCard
2510  * @name: the name of the attribute to get
2511  *
2512  * Get the attribute @name from @evc.  The #EVCardAttribute is owned by
2513  * @evcard and should not be freed. If the attribute does not exist, %NULL is
2514  * returned.
2515  *
2516  * <note><para>This will only return the <emphasis>first</emphasis> attribute
2517  * with the given @name. To get other attributes of that name (for example,
2518  * other <code>TEL</code> attributes if a contact has multiple telephone
2519  * numbers), use e_vcard_get_attributes() and iterate over the list searching
2520  * for matching attributes.</para>
2521  * <para>This method iterates over all attributes in the #EVCard, so should not
2522  * be called often. If extracting a large number of attributes from a vCard, it
2523  * is more efficient to iterate once over the list returned by
2524  * e_vcard_get_attributes().</para></note>
2525  *
2526  * Returns: (transfer none) (nullable): An #EVCardAttribute if found, or %NULL.
2527  **/
2528 EVCardAttribute *
2529 e_vcard_get_attribute (EVCard *evc,
2530                        const gchar *name)
2531 {
2532 	GList *attrs, *l;
2533 	EVCardAttribute *attr;
2534 
2535 	g_return_val_if_fail (E_IS_VCARD (evc), NULL);
2536 	g_return_val_if_fail (name != NULL, NULL);
2537 
2538 	/* Handle UID special case */
2539 	if (evc->priv->vcard != NULL && g_ascii_strcasecmp (name, EVC_UID) == 0) {
2540 		for (l = evc->priv->attributes; l != NULL; l = l->next) {
2541 			attr = (EVCardAttribute *) l->data;
2542 			if (g_ascii_strcasecmp (attr->name, name) == 0)
2543 				return attr;
2544 		}
2545 	}
2546 
2547 	attrs = e_vcard_ensure_attributes (evc);
2548 	for (l = attrs; l; l = l->next) {
2549 		attr = (EVCardAttribute *) l->data;
2550 		if (g_ascii_strcasecmp (attr->name, name) == 0)
2551 			return attr;
2552 	}
2553 
2554 	return NULL;
2555 }
2556 
2557 /**
2558  * e_vcard_get_attribute_if_parsed:
2559  * @evc: an #EVCard
2560  * @name: the name of the attribute to get
2561  *
2562  * Similar to e_vcard_get_attribute() but this method will not attempt to
2563  * parse the vCard if it is not already parsed.
2564  *
2565  * Returns: (transfer none) (nullable): An #EVCardAttribute if found, or %NULL.
2566  *
2567  * Since: 3.4
2568  **/
2569 EVCardAttribute *
2570 e_vcard_get_attribute_if_parsed (EVCard *evc,
2571                                  const gchar *name)
2572 {
2573 	GList *l;
2574 	EVCardAttribute *attr;
2575 
2576 	g_return_val_if_fail (E_IS_VCARD (evc), NULL);
2577 	g_return_val_if_fail (name != NULL, NULL);
2578 
2579 	for (l = evc->priv->attributes; l != NULL; l = l->next) {
2580 		attr = (EVCardAttribute *) l->data;
2581 		if (g_ascii_strcasecmp (attr->name, name) == 0)
2582 			return attr;
2583 	}
2584 
2585 	return NULL;
2586 }
2587 
2588 /**
2589  * e_vcard_attribute_get_group:
2590  * @attr: an #EVCardAttribute
2591  *
2592  * Gets the group name of @attr.
2593  *
2594  * Returns: (nullable): The attribute's group name, or %NULL.
2595  **/
2596 const gchar *
2597 e_vcard_attribute_get_group (EVCardAttribute *attr)
2598 {
2599 	g_return_val_if_fail (attr != NULL, NULL);
2600 
2601 	return attr->group;
2602 }
2603 
2604 /**
2605  * e_vcard_attribute_get_name:
2606  * @attr: an #EVCardAttribute
2607  *
2608  * Gets the name of @attr.
2609  *
2610  * Returns: The attribute's name.
2611  **/
2612 const gchar *
2613 e_vcard_attribute_get_name (EVCardAttribute *attr)
2614 {
2615 	g_return_val_if_fail (attr != NULL, NULL);
2616 
2617 	return attr->name;
2618 }
2619 
2620 /**
2621  * e_vcard_attribute_get_values:
2622  * @attr: an #EVCardAttribute
2623  *
2624  * Gets the ordered list of values from @attr. The list and its
2625  * contents are owned by @attr, and must not be freed.
2626  *
2627  * For example, for an <code>ADR</code> (postal address) attribute, this will
2628  * return the components of the postal address.
2629  *
2630  * This may be called on a single-valued attribute (i.e. one for which
2631  * e_vcard_attribute_is_single_valued() returns %TRUE) and will return a
2632  * one-element list in that case. Alternatively, use
2633  * e_vcard_attribute_get_value() in such cases.
2634  *
2635  * Returns: (transfer none) (element-type utf8): A list of string values. They
2636  * will all be non-%NULL, but may be empty strings. The list itself may be
2637  * empty.
2638  **/
2639 GList *
2640 e_vcard_attribute_get_values (EVCardAttribute *attr)
2641 {
2642 	g_return_val_if_fail (attr != NULL, NULL);
2643 
2644 	return attr->values;
2645 }
2646 
2647 /**
2648  * e_vcard_attribute_get_values_decoded:
2649  * @attr: an #EVCardAttribute
2650  *
2651  * Gets the ordered list of values from @attr, decoding them if
2652  * necessary according to the encoding given in the vCard’s
2653  * <code>ENCODING</code> attribute. The list and its contents are owned by
2654  * @attr, and must not be freed.
2655  *
2656  * This may be called on a single-valued attribute (i.e. one for which
2657  * e_vcard_attribute_is_single_valued() returns %TRUE) and will return a
2658  * one-element list in that case. Alternatively, use
2659  * e_vcard_attribute_get_value_decoded() in such cases.
2660  *
2661  * Returns: (transfer none) (element-type GString): A list of values of type #GString.
2662  **/
2663 GList *
2664 e_vcard_attribute_get_values_decoded (EVCardAttribute *attr)
2665 {
2666 	g_return_val_if_fail (attr != NULL, NULL);
2667 
2668 	if (!attr->decoded_values) {
2669 		GList *l;
2670 		switch (attr->encoding) {
2671 		case EVC_ENCODING_RAW:
2672 			for (l = attr->values; l; l = l->next)
2673 				attr->decoded_values = g_list_prepend (attr->decoded_values, g_string_new ((gchar *) l->data));
2674 			attr->decoded_values = g_list_reverse (attr->decoded_values);
2675 			break;
2676 		case EVC_ENCODING_BASE64:
2677 			for (l = attr->values; l; l = l->next) {
2678 				guchar *decoded;
2679 				gsize len = 0;
2680 
2681 				decoded = g_base64_decode (l->data, &len);
2682 				attr->decoded_values = g_list_prepend (attr->decoded_values, g_string_new_len ((gchar *) decoded, len));
2683 				g_free (decoded);
2684 			}
2685 			attr->decoded_values = g_list_reverse (attr->decoded_values);
2686 			break;
2687 		case EVC_ENCODING_QP:
2688 			for (l = attr->values; l; l = l->next) {
2689 				gchar *decoded;
2690 
2691 				decoded = e_vcard_qp_decode (l->data);
2692 				attr->decoded_values = g_list_prepend (attr->decoded_values, g_string_new (decoded));
2693 				g_free (decoded);
2694 			}
2695 			attr->decoded_values = g_list_reverse (attr->decoded_values);
2696 			break;
2697 		}
2698 	}
2699 
2700 	return attr->decoded_values;
2701 }
2702 
2703 /**
2704  * e_vcard_attribute_is_single_valued:
2705  * @attr: an #EVCardAttribute
2706  *
2707  * Checks if @attr has a single value.
2708  *
2709  * Returns: %TRUE if the attribute has exactly one value, %FALSE otherwise.
2710  **/
2711 gboolean
2712 e_vcard_attribute_is_single_valued (EVCardAttribute *attr)
2713 {
2714 	g_return_val_if_fail (attr != NULL, FALSE);
2715 
2716 	if (attr->values == NULL
2717 	    || attr->values->next != NULL)
2718 		return FALSE;
2719 
2720 	return TRUE;
2721 }
2722 
2723 /**
2724  * e_vcard_attribute_get_value:
2725  * @attr: an #EVCardAttribute
2726  *
2727  * Gets the value of a single-valued #EVCardAttribute, @attr.
2728  *
2729  * For example, for a <code>FN</code> (full name) attribute, this will
2730  * return the contact’s full name as a single string.
2731  *
2732  * This will print a warning if called on an #EVCardAttribute which is not
2733  * single-valued (i.e. for which e_vcard_attribute_is_single_valued() returns
2734  * %FALSE). Use e_vcard_attribute_get_values() in such cases instead.
2735  *
2736  * Returns: (nullable): A newly allocated string representing
2737  * the value, or %NULL if the attribute has no value.
2738  **/
2739 gchar *
2740 e_vcard_attribute_get_value (EVCardAttribute *attr)
2741 {
2742 	GList *values;
2743 
2744 	g_return_val_if_fail (attr != NULL, NULL);
2745 
2746 	values = e_vcard_attribute_get_values (attr);
2747 
2748 	if (!e_vcard_attribute_is_single_valued (attr))
2749 		g_warning ("e_vcard_attribute_get_value called on multivalued attribute");
2750 
2751 	return values ? g_strdup ((gchar *) values->data) : NULL;
2752 }
2753 
2754 /**
2755  * e_vcard_attribute_get_value_decoded:
2756  * @attr: an #EVCardAttribute
2757  *
2758  * Gets the value of a single-valued #EVCardAttribute, @attr, decoding
2759  * it if necessary according to the encoding given in the vCard’s
2760  * <code>ENCODING</code> attribute.
2761  *
2762  * This will print a warning if called on an #EVCardAttribute which is not
2763  * single-valued (i.e. for which e_vcard_attribute_is_single_valued() returns
2764  * %FALSE). Use e_vcard_attribute_get_values_decoded() in such cases instead.
2765  *
2766  * Returns: (nullable) (transfer full): A newly allocated #GString
2767  * representing the value, or %NULL if the attribute has no value.
2768  **/
2769 GString *
2770 e_vcard_attribute_get_value_decoded (EVCardAttribute *attr)
2771 {
2772 	GList *values;
2773 	GString *str = NULL;
2774 
2775 	g_return_val_if_fail (attr != NULL, NULL);
2776 
2777 	values = e_vcard_attribute_get_values_decoded (attr);
2778 
2779 	if (!e_vcard_attribute_is_single_valued (attr))
2780 		g_warning ("e_vcard_attribute_get_value_decoded called on multivalued attribute");
2781 
2782 	if (values)
2783 		str = values->data;
2784 
2785 	return str ? g_string_new_len (str->str, str->len) : NULL;
2786 }
2787 
2788 /**
2789  * e_vcard_attribute_has_type:
2790  * @attr: an #EVCardAttribute
2791  * @typestr: a string representing the type
2792  *
2793  * Checks if @attr has an #EVCardAttributeParam with name %EVC_TYPE and @typestr
2794  * as one of its values.
2795  *
2796  * For example, for the vCard attribute:
2797  * |[
2798  * TEL;TYPE=WORK,VOICE:(111) 555-1212
2799  * ]|
2800  * the following holds true:
2801  * |[
2802  * g_assert (e_vcard_attribute_has_type (attr, "WORK") == TRUE);
2803  * g_assert (e_vcard_attribute_has_type (attr, "voice") == TRUE);
2804  * g_assert (e_vcard_attribute_has_type (attr, "HOME") == FALSE);
2805  * ]|
2806  *
2807  * Comparisons against @typestr are case-insensitive.
2808  *
2809  * Returns: %TRUE if such a parameter exists, %FALSE otherwise.
2810  **/
2811 gboolean
2812 e_vcard_attribute_has_type (EVCardAttribute *attr,
2813                             const gchar *typestr)
2814 {
2815 	GList *params;
2816 	GList *p;
2817 
2818 	g_return_val_if_fail (attr != NULL, FALSE);
2819 	g_return_val_if_fail (typestr != NULL, FALSE);
2820 
2821 	params = e_vcard_attribute_get_params (attr);
2822 
2823 	for (p = params; p; p = p->next) {
2824 		EVCardAttributeParam *param = p->data;
2825 
2826 		if (!g_ascii_strcasecmp (e_vcard_attribute_param_get_name (param), EVC_TYPE)) {
2827 			GList *values = e_vcard_attribute_param_get_values (param);
2828 			GList *v;
2829 
2830 			for (v = values; v; v = v->next) {
2831 				if (!g_ascii_strcasecmp ((gchar *) v->data, typestr))
2832 					return TRUE;
2833 			}
2834 		}
2835 	}
2836 
2837 	return FALSE;
2838 }
2839 
2840 /**
2841  * e_vcard_attribute_get_params:
2842  * @attr: an #EVCardAttribute
2843  *
2844  * Gets the list of parameters (of type #EVCardAttributeParam) from @attr. The
2845  * list and its contents are owned by @attr, and must not be freed.
2846  *
2847  * Returns: (transfer none) (element-type EVCardAttributeParam): A list of
2848  * elements of type #EVCardAttributeParam.
2849  **/
2850 GList *
2851 e_vcard_attribute_get_params (EVCardAttribute *attr)
2852 {
2853 	g_return_val_if_fail (attr != NULL, NULL);
2854 
2855 	return attr->params;
2856 }
2857 
2858 /**
2859  * e_vcard_attribute_get_param:
2860  * @attr: an #EVCardAttribute
2861  * @name: a parameter name
2862  *
2863  * Gets the list of values for the paramater @name from @attr. The list and its
2864  * contents are owned by @attr, and must not be freed. If no parameter with the
2865  * given @name exists, %NULL is returned.
2866  *
2867  * Returns: (transfer none) (element-type utf8) (nullable): A list of string
2868  * elements representing the parameter's values, or %NULL.
2869  **/
2870 GList *
2871 e_vcard_attribute_get_param (EVCardAttribute *attr,
2872                              const gchar *name)
2873 {
2874 	GList *params, *p;
2875 
2876 	g_return_val_if_fail (attr != NULL, NULL);
2877 	g_return_val_if_fail (name != NULL, NULL);
2878 
2879 	params = e_vcard_attribute_get_params (attr);
2880 
2881 	for (p = params; p; p = p->next) {
2882 		EVCardAttributeParam *param = p->data;
2883 		if (g_ascii_strcasecmp (e_vcard_attribute_param_get_name (param), name) == 0) {
2884 			return e_vcard_attribute_param_get_values (param);
2885 		}
2886 	}
2887 
2888 	return NULL;
2889 }
2890 
2891 /**
2892  * e_vcard_is_parsed:
2893  * @evc: an #EVCard
2894  *
2895  * Check if the @evc has been parsed already, as #EVCard implements lazy parsing
2896  * of its vCard data. Used for debugging.
2897  *
2898  * Return value: %TRUE if @evc has been parsed, %FALSE otherwise.
2899  *
2900  * Since: 3.2
2901  **/
2902 gboolean
2903 e_vcard_is_parsed (EVCard *evc)
2904 {
2905 	g_return_val_if_fail (E_IS_VCARD (evc), FALSE);
2906 
2907 	return (!evc->priv->vcard && evc->priv->attributes);
2908 }
2909 
2910 /**
2911  * e_vcard_attribute_param_get_name:
2912  * @param: an #EVCardAttributeParam
2913  *
2914  * Gets the name of @param.
2915  *
2916  * For example, for the only parameter of the vCard attribute:
2917  * |[
2918  * TEL;TYPE=WORK,VOICE:(111) 555-1212
2919  * ]|
2920  * this would return <code>TYPE</code> (which is string-equivalent to
2921  * %EVC_TYPE).
2922  *
2923  * Returns: The name of the parameter.
2924  **/
2925 const gchar *
2926 e_vcard_attribute_param_get_name (EVCardAttributeParam *param)
2927 {
2928 	g_return_val_if_fail (param != NULL, NULL);
2929 
2930 	return param->name;
2931 }
2932 
2933 /**
2934  * e_vcard_attribute_param_get_values:
2935  * @param: an #EVCardAttributeParam
2936  *
2937  * Gets the list of values from @param. The list and its
2938  * contents are owned by @param, and must not be freed.
2939  *
2940  * For example, for the <code>TYPE</code> parameter of the vCard attribute:
2941  * |[
2942  * TEL;TYPE=WORK,VOICE:(111) 555-1212
2943  * ]|
2944  * this would return the list <code>WORK</code>, <code>VOICE</code>.
2945  *
2946  * Returns: (transfer none) (element-type utf8): A list of string elements
2947  * representing the parameter's values.
2948  **/
2949 GList *
2950 e_vcard_attribute_param_get_values (EVCardAttributeParam *param)
2951 {
2952 	g_return_val_if_fail (param != NULL, NULL);
2953 
2954 	return param->values;
2955 }
2956 
2957 /**
2958  * e_vcard_util_set_x_attribute:
2959  * @vcard: an #EVCard
2960  * @x_name: the attribute name, which starts with "X-"
2961  * @value: (nullable): the value to set, or %NULL to unset
2962  *
2963  * Sets an "X-" attribute @x_name to value @value in @vcard, or
2964  * removes it from @vcard, when @value is %NULL.
2965  *
2966  * Since: 3.26
2967  **/
2968 void
2969 e_vcard_util_set_x_attribute (EVCard *vcard,
2970 			      const gchar *x_name,
2971 			      const gchar *value)
2972 {
2973 	EVCardAttribute *attr;
2974 
2975 	g_return_if_fail (E_IS_VCARD (vcard));
2976 	g_return_if_fail (x_name != NULL);
2977 	g_return_if_fail (g_str_has_prefix (x_name, "X-"));
2978 
2979 	attr = e_vcard_get_attribute (vcard, x_name);
2980 
2981 	if (attr) {
2982 		e_vcard_attribute_remove_values (attr);
2983 		if (value) {
2984 			e_vcard_attribute_add_value (attr, value);
2985 		} else {
2986 			e_vcard_remove_attribute (vcard, attr);
2987 		}
2988 	} else if (value) {
2989 		e_vcard_append_attribute_with_value (
2990 			vcard,
2991 			e_vcard_attribute_new (NULL, x_name),
2992 			value);
2993 	}
2994 }
2995 
2996 /**
2997  * e_vcard_util_dup_x_attribute:
2998  * @vcard: an #EVCard
2999  * @x_name: the attribute name, which starts with "X-"
3000  *
3001  * Returns: (nullable): Value of attribute @x_name, or %NULL,
3002  *    when there is no such attribute. Free the returned pointer with g_free(),
3003  *    when no longer needed.
3004  *
3005  * Since: 3.26
3006  **/
3007 gchar *
3008 e_vcard_util_dup_x_attribute (EVCard *vcard,
3009 			      const gchar *x_name)
3010 {
3011 	EVCardAttribute *attr;
3012 	GList *v = NULL;
3013 
3014 	g_return_val_if_fail (E_IS_VCARD (vcard), NULL);
3015 	g_return_val_if_fail (x_name != NULL, NULL);
3016 	g_return_val_if_fail (g_str_has_prefix (x_name, "X-"), NULL);
3017 
3018 	attr = e_vcard_get_attribute (vcard, x_name);
3019 
3020 	if (attr)
3021 		v = e_vcard_attribute_get_values (attr);
3022 
3023 	return ((v && v->data) ? g_strstrip (g_strdup (v->data)) : NULL);
3024 }
3025