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 (¶m->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 (¶m->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