tizen beta release
[profile/ivi/obexd.git] / plugins / vcard.c
1 /*
2  * OBEX Server
3  *
4  * Copyright (C) 2008-2010 Intel Corporation.  All rights reserved.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <string.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <stdint.h>
30 #include <ctype.h>
31 #include <errno.h>
32
33 #include <glib.h>
34 #include <gdbus.h>
35
36 #include "vcard.h"
37 #include "glib-helper.h"
38
39 #define ADDR_FIELD_AMOUNT 7
40 #define LEN_MAX 128
41 #define TYPE_INTERNATIONAL 145
42
43 #define PHONEBOOK_FLAG_CACHED 0x1
44
45 #define FILTER_VERSION (1 << 0)
46 #define FILTER_FN (1 << 1)
47 #define FILTER_N (1 << 2)
48 #define FILTER_PHOTO (1 << 3)
49 #define FILTER_BDAY (1 << 4)
50 #define FILTER_ADR (1 << 5)
51 #define FILTER_LABEL (1 << 6)
52 #define FILTER_TEL (1 << 7)
53 #define FILTER_EMAIL (1 << 8)
54 #define FILTER_MAILER (1 << 9)
55 #define FILTER_TZ (1 << 10)
56 #define FILTER_GEO (1 << 11)
57 #define FILTER_TITLE (1 << 12)
58 #define FILTER_ROLE (1 << 13)
59 #define FILTER_LOGO (1 << 14)
60 #define FILTER_AGENT (1 << 15)
61 #define FILTER_ORG (1 << 16)
62 #define FILTER_NOTE (1 << 17)
63 #define FILTER_REV (1 << 18)
64 #define FILTER_SOUND (1 << 19)
65 #define FILTER_URL (1 << 20)
66 #define FILTER_UID (1 << 21)
67 #define FILTER_KEY (1 << 22)
68 #define FILTER_NICKNAME (1 << 23)
69 #define FILTER_CATEGORIES (1 << 24)
70 #define FILTER_PROID (1 << 25)
71 #define FILTER_CLASS (1 << 26)
72 #define FILTER_SORT_STRING (1 << 27)
73 #define FILTER_X_IRMC_CALL_DATETIME (1 << 28)
74
75 #define FORMAT_VCARD21 0x00
76 #define FORMAT_VCARD30 0x01
77
78 #define QP_LINE_LEN 75
79 #define QP_CHAR_LEN 3
80 #define QP_CR 0x0D
81 #define QP_LF 0x0A
82 #define QP_ESC 0x5C
83 #define QP_SOFT_LINE_BREAK "="
84 #define QP_SELECT "\n!\"#$=@[\\]^`{|}~"
85 #define ASCII_LIMIT 0x7F
86
87 /* according to RFC 2425, the output string may need folding */
88 static void vcard_printf(GString *str, const char *fmt, ...)
89 {
90         char buf[1024];
91         va_list ap;
92         int len_temp, line_number, i;
93         unsigned int line_delimit = 75;
94
95         va_start(ap, fmt);
96         vsnprintf(buf, sizeof(buf), fmt, ap);
97         va_end(ap);
98
99         line_number = strlen(buf) / line_delimit + 1;
100
101         for (i = 0; i < line_number; i++) {
102                 len_temp = MIN(line_delimit, strlen(buf) - line_delimit * i);
103                 g_string_append_len(str,  buf + line_delimit * i, len_temp);
104                 if (i != line_number - 1)
105                         g_string_append(str, "\r\n ");
106         }
107
108         g_string_append(str, "\r\n");
109 }
110
111 /* According to RFC 2426, we need escape following characters:
112  *  '\n', '\r', ';', ',', '\'.
113  */
114 static void add_slash(char *dest, const char *src, int len_max, int len)
115 {
116         int i, j;
117
118         for (i = 0, j = 0; i < len && j + 1 < len_max; i++, j++) {
119                 /* filling dest buffer - last field need to be reserved
120                  * for '\0'*/
121                 switch (src[i]) {
122                 case '\n':
123                         if (j + 2 >= len_max)
124                                 /* not enough space in the buffer to put char
125                                  * preceded with escaping sequence (and '\0' in
126                                  * the end) */
127                                 goto done;
128
129                         dest[j++] = '\\';
130                         dest[j] = 'n';
131                         break;
132                 case '\r':
133                         if (j + 2 >= len_max)
134                                 goto done;
135
136                         dest[j++] = '\\';
137                         dest[j] = 'r';
138                         break;
139                 case '\\':
140                 case ';':
141                 case ',':
142                         if (j + 2 >= len_max)
143                                 goto done;
144
145                         dest[j++] = '\\';
146                 default:
147                         dest[j] = src[i];
148                         break;
149                 }
150         }
151
152 done:
153         dest[j] = 0;
154 }
155
156 static void escape_semicolon(char *dest, const char *src, int len_max, int len)
157 {
158         int i, j;
159
160         for (i = 0, j = 0; i < len && j + 1 < len_max; i++, j++) {
161                 if (src[i] == ';') {
162                         if (j + 2 >= len_max)
163                                 break;
164
165                         dest[j++] = '\\';
166                 }
167
168                 dest[j] = src[i];
169         }
170
171         dest[j] = 0;
172 }
173
174 static void set_escape(uint8_t format, char *dest, const char *src,
175                                                         int len_max, int len)
176 {
177         if (format == FORMAT_VCARD30)
178                 add_slash(dest, src, len_max, len);
179         else if (format == FORMAT_VCARD21)
180                 escape_semicolon(dest, src, len_max, len);
181 }
182
183 static void get_escaped_fields(uint8_t format, char **fields, ...)
184 {
185         va_list ap;
186         GString *line;
187         char *field;
188         char escaped[LEN_MAX];
189
190         va_start(ap, fields);
191         line = g_string_new("");
192
193         for (field = va_arg(ap, char *); field; ) {
194                 set_escape(format, escaped, field, LEN_MAX, strlen(field));
195                 g_string_append(line, escaped);
196
197                 field = va_arg(ap, char *);
198
199                 if (field)
200                         g_string_append(line, ";");
201         }
202
203         va_end(ap);
204
205         *fields = g_string_free(line, FALSE);
206 }
207
208 static gboolean set_qp_encoding(char c)
209 {
210         unsigned char q = c;
211
212         if (strchr(QP_SELECT, q) != NULL)
213                 return TRUE;
214
215         if (q < '!' || q > '~')
216                 return TRUE;
217
218         return FALSE;
219 }
220
221 static void append_qp_break_line(GString *vcards, size_t *limit)
222 {
223         /* Quoted Printable lines of text must be limited to less than 76
224          * characters and terminated by Quoted Printable softline break
225          * sequence of "=" (if some more characters left) */
226         g_string_append(vcards, QP_SOFT_LINE_BREAK);
227         g_string_append(vcards, "\r\n ");
228         *limit = QP_LINE_LEN - 1;
229 }
230
231 static void append_qp_ascii(GString *vcards, size_t *limit, char c)
232 {
233         if (*limit == 0)
234                 append_qp_break_line(vcards, limit);
235
236         g_string_append_c(vcards, c);
237         --*limit;
238 }
239
240 static void append_qp_hex(GString *vcards, size_t *limit, char c)
241 {
242         if (*limit < QP_CHAR_LEN)
243                 append_qp_break_line(vcards, limit);
244
245         g_string_append_printf(vcards, "=%2.2X", (unsigned char) c);
246         *limit -= QP_CHAR_LEN;
247 }
248
249 static void append_qp_new_line(GString *vcards, size_t *limit)
250 {
251         /* Multiple lines of text are separated with a Quoted Printable CRLF
252          * sequence of "=0D" followed by "=0A" followed by a Quoted Printable
253          * softline break sequence of "=" */
254         append_qp_hex(vcards, limit, QP_CR);
255         append_qp_hex(vcards, limit, QP_LF);
256         append_qp_break_line(vcards, limit);
257 }
258
259 static gboolean utf8_select(const char *field)
260 {
261         const char *pos;
262
263         if (g_utf8_validate(field, -1, NULL) == FALSE)
264                 return FALSE;
265
266         for (pos = field; *pos != '\0'; pos = g_utf8_next_char(pos)) {
267                 /* Test for non-standard UTF-8 character (out of range
268                  * standard ASCII set), composed of more than single byte
269                  * and represented by 32-bit value greater than 0x7F */
270                 if (g_utf8_get_char(pos) > ASCII_LIMIT)
271                         return TRUE;
272         }
273
274         return FALSE;
275 }
276
277 static void vcard_qp_print_encoded(GString *vcards, const char *desc, ...)
278 {
279         const char *field, *charset = "";
280         const char *encoding = ";ENCODING=QUOTED-PRINTABLE";
281         size_t limit, param_len;
282         va_list ap;
283
284         va_start(ap, desc);
285
286         for (field = va_arg(ap, char *); field; field = va_arg(ap, char *)) {
287                 if (utf8_select(field) == TRUE) {
288                         charset = ";CHARSET=UTF-8";
289                         break;
290                 }
291         }
292
293         va_end(ap);
294
295         vcard_printf(vcards, "%s%s%s:", desc, encoding, charset);
296         g_string_truncate(vcards, vcards->len - 2);
297
298         param_len = strlen(desc) + strlen(encoding) + strlen(charset) + 1;
299         limit = QP_LINE_LEN - param_len;
300
301         va_start(ap, desc);
302
303         for (field = va_arg(ap, char *); field != NULL; ) {
304                 size_t i, size = strlen(field);
305
306                 for (i = 0; i < size; ++i) {
307                         if (set_qp_encoding(field[i])) {
308                                 if (field[i] == '\n') {
309                                         append_qp_new_line(vcards, &limit);
310                                         continue;
311                                 }
312
313                                 append_qp_hex(vcards, &limit, field[i]);
314                         } else {
315                                 /* According to vCard 2.1 spec. semicolons in
316                                  * property parameter value must be escaped */
317                                 if (field[i] == ';')
318                                         append_qp_hex(vcards, &limit, QP_ESC);
319
320                                 append_qp_ascii(vcards, &limit, field[i]);
321                         }
322                 }
323
324                 field = va_arg(ap, char *);
325                 if (field)
326                         append_qp_ascii(vcards, &limit, ';');
327         }
328
329         va_end(ap);
330
331         g_string_append(vcards, "\r\n");
332 }
333
334 static gboolean select_qp_encoding(uint8_t format, ...)
335 {
336         char *field;
337         va_list ap;
338
339         if (format != FORMAT_VCARD21)
340                 return FALSE;
341
342         va_start(ap, format);
343
344         for (field = va_arg(ap, char *); field; field = va_arg(ap, char *)) {
345                 int i;
346                 unsigned char c;
347
348                 if (strpbrk(field, QP_SELECT)) {
349                         va_end(ap);
350                         return TRUE;
351                 }
352
353                 /* Quoted Printable encoding is selected if there is
354                  * a character, which value is out of range standard
355                  * ASCII set, since it may be a part of some
356                  * non-standard character such as specified by UTF-8 */
357                 for (i = 0; (c = field[i]) != '\0'; ++i) {
358                         if (c > ASCII_LIMIT) {
359                                 va_end(ap);
360                                 return TRUE;
361                         }
362                 }
363         }
364
365         va_end(ap);
366
367         return FALSE;
368 }
369
370 static void vcard_printf_begin(GString *vcards, uint8_t format)
371 {
372         vcard_printf(vcards, "BEGIN:VCARD");
373
374         if (format == FORMAT_VCARD30)
375                 vcard_printf(vcards, "VERSION:3.0");
376         else if (format == FORMAT_VCARD21)
377                 vcard_printf(vcards, "VERSION:2.1");
378 }
379
380 /* check if there is at least one contact field with personal data present */
381 static gboolean contact_fields_present(struct phonebook_contact * contact)
382 {
383         if (contact->family && strlen(contact->family) > 0)
384                 return TRUE;
385
386         if (contact->given && strlen(contact->given) > 0)
387                 return TRUE;
388
389         if (contact->additional && strlen(contact->additional) > 0)
390                 return TRUE;
391
392         if (contact->prefix && strlen(contact->prefix) > 0)
393                 return TRUE;
394
395         if (contact->suffix && strlen(contact->suffix) > 0)
396                 return TRUE;
397
398         /* none of the personal data fields are present*/
399         return FALSE;
400 }
401
402 static void vcard_printf_name(GString *vcards, uint8_t format,
403                                         struct phonebook_contact *contact)
404 {
405         char *fields;
406
407         if (contact_fields_present(contact) == FALSE) {
408                 /* If fields are empty, add only 'N:' as parameter.
409                  * This is crucial for some devices (Nokia BH-903) which
410                  * have problems with history listings and can't determine
411                  * that a parameter is really empty if there are unnecessary
412                  * characters after 'N:' (e.g. 'N:;;;;').
413                  * We need to add only'N:' param - without semicolons.
414                  */
415                 vcard_printf(vcards, "N:");
416                 return;
417         }
418
419         if (select_qp_encoding(format, contact->family, contact->given,
420                                         contact->additional, contact->prefix,
421                                         contact->suffix, NULL)) {
422                 vcard_qp_print_encoded(vcards, "N", contact->family,
423                                         contact->given, contact->additional,
424                                         contact->prefix, contact->suffix,
425                                         NULL);
426                 return;
427         }
428
429         get_escaped_fields(format, &fields, contact->family,
430                                 contact->given, contact->additional,
431                                 contact->prefix, contact->suffix,
432                                 NULL);
433
434         vcard_printf(vcards, "N:%s", fields);
435
436         g_free(fields);
437 }
438
439 static void vcard_printf_fullname(GString *vcards, uint8_t format,
440                                                         const char *text)
441 {
442         char field[LEN_MAX];
443
444         if (!text || strlen(text) == 0) {
445                 vcard_printf(vcards, "FN:");
446                 return;
447         }
448
449         if (select_qp_encoding(format, text, NULL)) {
450                 vcard_qp_print_encoded(vcards, "FN", text, NULL);
451                 return;
452         }
453
454         set_escape(format, field, text, LEN_MAX, strlen(text));
455         vcard_printf(vcards, "FN:%s", field);
456 }
457
458 static void vcard_printf_number(GString *vcards, uint8_t format,
459                                         const char *number, int type,
460                                         enum phonebook_number_type category)
461 {
462         const char *intl = "", *category_string = "";
463         char buf[LEN_MAX], field[LEN_MAX];
464
465         /* TEL is a mandatory field, include even if empty */
466         if (!number || !strlen(number) || !type) {
467                 vcard_printf(vcards, "TEL:");
468                 return;
469         }
470
471         switch (category) {
472         case TEL_TYPE_HOME:
473                 if (format == FORMAT_VCARD21)
474                         category_string = "HOME;VOICE";
475                 else if (format == FORMAT_VCARD30)
476                         category_string = "TYPE=HOME;TYPE=VOICE";
477                 break;
478         case TEL_TYPE_MOBILE:
479                 if (format == FORMAT_VCARD21)
480                         category_string = "CELL;VOICE";
481                 else if (format == FORMAT_VCARD30)
482                         category_string = "TYPE=CELL;TYPE=VOICE";
483                 break;
484         case TEL_TYPE_FAX:
485                 if (format == FORMAT_VCARD21)
486                         category_string = "FAX";
487                 else if (format == FORMAT_VCARD30)
488                         category_string = "TYPE=FAX";
489                 break;
490         case TEL_TYPE_WORK:
491                 if (format == FORMAT_VCARD21)
492                         category_string = "WORK;VOICE";
493                 else if (format == FORMAT_VCARD30)
494                         category_string = "TYPE=WORK;TYPE=VOICE";
495                 break;
496         case TEL_TYPE_OTHER:
497                 if (format == FORMAT_VCARD21)
498                         category_string = "OTHER;VOICE";
499                 else if (format == FORMAT_VCARD30)
500                         category_string = "TYPE=OTHER;TYPE=VOICE";
501                 break;
502         }
503
504         if ((type == TYPE_INTERNATIONAL) && (number[0] != '+'))
505                 intl = "+";
506
507         snprintf(field, sizeof(field), "%s%s", intl, number);
508
509         if (select_qp_encoding(format, number, NULL)) {
510                 snprintf(buf, sizeof(buf), "TEL;%s", category_string);
511                 vcard_qp_print_encoded(vcards, buf, field, NULL);
512                 return;
513         }
514
515         vcard_printf(vcards, "TEL;%s:%s", category_string, field);
516 }
517
518 static void vcard_printf_tag(GString *vcards, uint8_t format,
519                                         const char *tag, const char *category,
520                                         const char *fld)
521 {
522         int len;
523         char *separator = "", *type = "";
524         char buf[LEN_MAX], field[LEN_MAX];
525
526         if (tag == NULL || strlen(tag) == 0)
527                 return;
528
529         if (fld == NULL || (len = strlen(fld)) == 0) {
530                 vcard_printf(vcards, "%s:", tag);
531                 return;
532         }
533
534         if (category && strlen(category)) {
535                 separator = ";";
536                 if (format == FORMAT_VCARD30)
537                         type = "TYPE=";
538         } else {
539                 category = "";
540         }
541
542         snprintf(buf, LEN_MAX, "%s%s%s%s", tag, separator, type, category);
543
544         if (select_qp_encoding(format, fld, NULL)) {
545                 vcard_qp_print_encoded(vcards, buf, fld, NULL);
546                 return;
547         }
548
549         set_escape(format, field, fld, LEN_MAX, len);
550         vcard_printf(vcards, "%s:%s", buf, field);
551 }
552
553 static void vcard_printf_email(GString *vcards, uint8_t format,
554                                         const char *address,
555                                         enum phonebook_field_type category)
556 {
557         const char *category_string = "";
558         char buf[LEN_MAX], field[LEN_MAX];
559         int len = 0;
560
561         if (!address || !(len = strlen(address))) {
562                 vcard_printf(vcards, "EMAIL:");
563                 return;
564         }
565         switch (category) {
566         case FIELD_TYPE_HOME:
567                 if (format == FORMAT_VCARD21)
568                         category_string = "INTERNET;HOME";
569                 else if (format == FORMAT_VCARD30)
570                         category_string = "TYPE=INTERNET;TYPE=HOME";
571                 break;
572         case FIELD_TYPE_WORK:
573                 if (format == FORMAT_VCARD21)
574                         category_string = "INTERNET;WORK";
575                 else if (format == FORMAT_VCARD30)
576                         category_string = "TYPE=INTERNET;TYPE=WORK";
577                 break;
578         default:
579                 if (format == FORMAT_VCARD21)
580                         category_string = "INTERNET";
581                 else if (format == FORMAT_VCARD30)
582                         category_string = "TYPE=INTERNET;TYPE=OTHER";
583         }
584
585         if (select_qp_encoding(format, address, NULL)) {
586                 snprintf(buf, sizeof(buf), "EMAIL;%s", category_string);
587                 vcard_qp_print_encoded(vcards, buf, address, NULL);
588                 return;
589         }
590
591         set_escape(format, field, address, LEN_MAX, len);
592         vcard_printf(vcards, "EMAIL;%s:%s", category_string, field);
593 }
594
595 static void vcard_printf_url(GString *vcards, uint8_t format,
596                                         const char *url,
597                                         enum phonebook_field_type category)
598 {
599         const char *category_string = "";
600         char buf[LEN_MAX], field[LEN_MAX];
601
602         if (!url || strlen(url) == 0) {
603                 vcard_printf(vcards, "URL:");
604                 return;
605         }
606
607         switch (category) {
608         case FIELD_TYPE_HOME:
609                 if (format == FORMAT_VCARD21)
610                         category_string = "INTERNET;HOME";
611                 else if (format == FORMAT_VCARD30)
612                         category_string = "TYPE=INTERNET;TYPE=HOME";
613                 break;
614         case FIELD_TYPE_WORK:
615                 if (format == FORMAT_VCARD21)
616                         category_string = "INTERNET;WORK";
617                 else if (format == FORMAT_VCARD30)
618                         category_string = "TYPE=INTERNET;TYPE=WORK";
619                 break;
620         default:
621                 if (format == FORMAT_VCARD21)
622                         category_string = "INTERNET";
623                 else if (format == FORMAT_VCARD30)
624                         category_string = "TYPE=INTERNET";
625                 break;
626         }
627
628         if (select_qp_encoding(format, url, NULL)) {
629                 snprintf(buf, sizeof(buf), "URL;%s", category_string);
630                 vcard_qp_print_encoded(vcards, buf, url, NULL);
631                 return;
632         }
633
634         set_escape(format, field, url, LEN_MAX, strlen(url));
635         vcard_printf(vcards, "URL;%s:%s", category_string, field);
636 }
637
638 static gboolean org_fields_present(struct phonebook_contact *contact)
639 {
640         if (contact->company && strlen(contact->company))
641                 return TRUE;
642
643         if (contact->department && strlen(contact->department))
644                 return TRUE;
645
646         return FALSE;
647 }
648
649 static void vcard_printf_org(GString *vcards, uint8_t format,
650                                         struct phonebook_contact *contact)
651 {
652         char *fields;
653
654         if (org_fields_present(contact) == FALSE)
655                 return;
656
657         if (select_qp_encoding(format, contact->company,
658                                                 contact->department, NULL)) {
659                 vcard_qp_print_encoded(vcards, "ORG", contact->company,
660                                                 contact->department, NULL);
661                 return;
662         }
663
664         get_escaped_fields(format, &fields, contact->company,
665                                         contact->department, NULL);
666
667         vcard_printf(vcards, "ORG:%s", fields);
668
669         g_free(fields);
670 }
671
672 static void vcard_printf_address(GString *vcards, uint8_t format,
673                                         struct phonebook_addr *address)
674 {
675         char *fields, field_esc[LEN_MAX];
676         const char *category_string = "";
677         char buf[LEN_MAX], *address_fields[ADDR_FIELD_AMOUNT];
678         int i;
679         size_t len;
680         GSList *l;
681
682         if (!address) {
683                 vcard_printf(vcards, "ADR:");
684                 return;
685         }
686
687         switch (address->type) {
688         case FIELD_TYPE_HOME:
689                 if (format == FORMAT_VCARD21)
690                         category_string = "HOME";
691                 else if (format == FORMAT_VCARD30)
692                         category_string = "TYPE=HOME";
693                 break;
694         case FIELD_TYPE_WORK:
695                 if (format == FORMAT_VCARD21)
696                         category_string = "WORK";
697                 else if (format == FORMAT_VCARD30)
698                         category_string = "TYPE=WORK";
699                 break;
700         default:
701                 if (format == FORMAT_VCARD21)
702                         category_string = "OTHER";
703                 else if (format == FORMAT_VCARD30)
704                         category_string = "TYPE=OTHER";
705                 break;
706         }
707
708         for (i = 0, l = address->fields; l; l = l->next)
709                 address_fields[i++] = l->data;
710
711         if (select_qp_encoding(format, address_fields[0], address_fields[1],
712                                         address_fields[2], address_fields[3],
713                                         address_fields[4], address_fields[5],
714                                         address_fields[6], NULL)) {
715                 snprintf(buf, sizeof(buf), "ADR;%s", category_string);
716                 vcard_qp_print_encoded(vcards, buf,
717                                         address_fields[0], address_fields[1],
718                                         address_fields[2], address_fields[3],
719                                         address_fields[4], address_fields[5],
720                                         address_fields[6], NULL);
721                 return;
722         }
723
724         /* allocate enough memory to insert address fields separated by ';'
725          * and terminated by '\0' */
726         len = ADDR_FIELD_AMOUNT * LEN_MAX;
727         fields = g_malloc0(len);
728
729         for (l = address->fields; l; l = l->next) {
730                 char *field = l->data;
731
732                 if (field) {
733                         set_escape(format, field_esc, field, LEN_MAX,
734                                                                 strlen(field));
735                         g_strlcat(fields, field_esc, len);
736                 }
737
738                 if (l->next)
739                         /* not adding ';' after last addr field */
740                         g_strlcat(fields, ";", len);
741         }
742
743         vcard_printf(vcards,"ADR;%s:%s", category_string, fields);
744
745         g_free(fields);
746 }
747
748 static void vcard_printf_datetime(GString *vcards, uint8_t format,
749                                         struct phonebook_contact *contact)
750 {
751         const char *type;
752         char buf[LEN_MAX];
753
754         switch (contact->calltype) {
755         case CALL_TYPE_MISSED:
756                 type = "MISSED";
757                 break;
758
759         case CALL_TYPE_INCOMING:
760                 type = "RECEIVED";
761                 break;
762
763         case CALL_TYPE_OUTGOING:
764                 type = "DIALED";
765                 break;
766
767         case CALL_TYPE_NOT_A_CALL:
768         default:
769                 return;
770         }
771
772         if (select_qp_encoding(format, contact->datetime, NULL)) {
773                 snprintf(buf, sizeof(buf), "X-IRMC-CALL-DATETIME;%s", type);
774                 vcard_qp_print_encoded(vcards, buf, contact->datetime, NULL);
775                 return;
776         }
777
778         vcard_printf(vcards, "X-IRMC-CALL-DATETIME;%s:%s", type,
779                                                         contact->datetime);
780 }
781
782 static void vcard_printf_end(GString *vcards)
783 {
784         vcard_printf(vcards, "END:VCARD");
785 }
786
787 void phonebook_add_contact(GString *vcards, struct phonebook_contact *contact,
788                                         uint64_t filter, uint8_t format)
789 {
790         if (format == FORMAT_VCARD30 && filter)
791                 filter |= (FILTER_VERSION | FILTER_FN | FILTER_N | FILTER_TEL);
792         else if (format == FORMAT_VCARD21 && filter)
793                 filter |= (FILTER_VERSION | FILTER_N | FILTER_TEL);
794         else
795                 filter = (FILTER_VERSION | FILTER_UID | FILTER_N | FILTER_FN |
796                                 FILTER_TEL | FILTER_EMAIL | FILTER_ADR |
797                                 FILTER_BDAY | FILTER_NICKNAME | FILTER_URL |
798                                 FILTER_PHOTO | FILTER_ORG | FILTER_ROLE |
799                                 FILTER_TITLE | FILTER_X_IRMC_CALL_DATETIME);
800
801         vcard_printf_begin(vcards, format);
802
803         if (filter & FILTER_UID && *contact->uid)
804                 vcard_printf_tag(vcards, format, "UID", NULL, contact->uid);
805
806         if (filter & FILTER_N)
807                 vcard_printf_name(vcards, format, contact);
808
809         if (filter & FILTER_FN && (*contact->fullname ||
810                                         format == FORMAT_VCARD30))
811                 vcard_printf_fullname(vcards, format, contact->fullname);
812
813         if (filter & FILTER_TEL) {
814                 GSList *l = contact->numbers;
815
816                 if (g_slist_length(l) == 0)
817                         vcard_printf_number(vcards, format, NULL, 1,
818                                                         TEL_TYPE_OTHER);
819
820                 for (; l; l = l->next) {
821                         struct phonebook_field *number = l->data;
822
823                         vcard_printf_number(vcards, format, number->text, 1,
824                                                                 number->type);
825                 }
826         }
827
828         if (filter & FILTER_EMAIL) {
829                 GSList *l = contact->emails;
830
831                 for (; l; l = l->next) {
832                         struct phonebook_field *email = l->data;
833                         vcard_printf_email(vcards, format, email->text,
834                                                                 email->type);
835                 }
836         }
837
838         if (filter & FILTER_ADR) {
839                 GSList *l = contact->addresses;
840
841                 for (; l; l = l->next) {
842                         struct phonebook_addr *addr = l->data;
843                         vcard_printf_address(vcards, format, addr);
844                 }
845         }
846
847         if (filter & FILTER_BDAY && *contact->birthday)
848                 vcard_printf_tag(vcards, format, "BDAY", NULL,
849                                                 contact->birthday);
850
851         if (filter & FILTER_NICKNAME && *contact->nickname)
852                 vcard_printf_tag(vcards, format, "NICKNAME", NULL,
853                                                         contact->nickname);
854
855         if (filter & FILTER_URL) {
856                 GSList *l = contact->urls;
857
858                 for (; l; l = l->next) {
859                         struct phonebook_field *url = l->data;
860                         vcard_printf_url(vcards, format, url->text, url->type);
861                 }
862         }
863
864         if (filter & FILTER_PHOTO && *contact->photo)
865                 vcard_printf_tag(vcards, format, "PHOTO", NULL,
866                                                         contact->photo);
867
868         if (filter & FILTER_ORG)
869                 vcard_printf_org(vcards, format, contact);
870
871         if (filter & FILTER_ROLE && *contact->role)
872                 vcard_printf_tag(vcards, format, "ROLE", NULL, contact->role);
873
874         if (filter & FILTER_TITLE && *contact->title)
875                 vcard_printf_tag(vcards, format, "TITLE", NULL, contact->title);
876
877         if (filter & FILTER_X_IRMC_CALL_DATETIME)
878                 vcard_printf_datetime(vcards, format, contact);
879
880         vcard_printf_end(vcards);
881 }
882
883 static void field_free(gpointer data)
884 {
885         struct phonebook_field *field = data;
886
887         g_free(field->text);
888         g_free(field);
889 }
890
891 void phonebook_addr_free(gpointer addr)
892 {
893         struct phonebook_addr *address = addr;
894
895         g_slist_free_full(address->fields, g_free);
896         g_free(address);
897 }
898
899 void phonebook_contact_free(struct phonebook_contact *contact)
900 {
901         if (contact == NULL)
902                 return;
903
904         g_slist_free_full(contact->numbers, field_free);
905         g_slist_free_full(contact->emails, field_free);
906         g_slist_free_full(contact->addresses, phonebook_addr_free);
907         g_slist_free_full(contact->urls, field_free);
908
909         g_free(contact->uid);
910         g_free(contact->fullname);
911         g_free(contact->given);
912         g_free(contact->family);
913         g_free(contact->additional);
914         g_free(contact->prefix);
915         g_free(contact->suffix);
916         g_free(contact->birthday);
917         g_free(contact->nickname);
918         g_free(contact->photo);
919         g_free(contact->company);
920         g_free(contact->department);
921         g_free(contact->role);
922         g_free(contact->title);
923         g_free(contact->datetime);
924         g_free(contact);
925 }