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