5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 2002 Pekka Riikonen
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; version 2 of the License.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
20 /* Implementation of the VCard (RFC 2426) */
22 #include "silcincludes.h"
24 #define VCARD_HEADER "BEGIN:VCARD\n"
25 #define VCARD_VERSION "VERSION:3.0\n"
26 #define VCARD_FOOTER "END:VCARD\n"
30 unsigned char *silc_vcard_encode(SilcVCard vcard, SilcUInt32 *vcard_len)
32 SilcBufferStruct buffer;
35 if (!vcard->full_name || !vcard->family_name || !vcard->first_name)
38 memset(&buffer, 0, sizeof(buffer));
39 silc_buffer_strformat(
43 "FN:", vcard->full_name, "\n",
44 "N:", vcard->family_name, ";", vcard->first_name, ";",
45 vcard->middle_names, ";", vcard->prefix, ";", vcard->suffix, "\n",
49 silc_buffer_strformat(&buffer,
50 "NICKNAME:", vcard->nickname, "\n",
53 silc_buffer_strformat(&buffer,
54 "BDAY:", vcard->bday, "\n",
57 silc_buffer_strformat(&buffer,
58 "TITLE:", vcard->title, "\n",
61 silc_buffer_strformat(&buffer,
62 "ROLE:", vcard->role, "\n",
65 silc_buffer_strformat(&buffer,
66 "ORG:", vcard->org_name, ";", vcard->org_unit, "\n",
68 if (vcard->categories)
69 silc_buffer_strformat(&buffer,
70 "CATEGORIES:", vcard->categories, "\n",
73 silc_buffer_strformat(&buffer,
74 "CLASS:", vcard->class, "\n",
77 silc_buffer_strformat(&buffer,
78 "URL:", vcard->url, "\n",
81 silc_buffer_strformat(&buffer,
82 "LABEL;", vcard->url, "\n",
84 for (i = 0; i < vcard->num_addrs; i++) {
85 silc_buffer_strformat(&buffer,
87 vcard->addrs[i].type, ":",
88 vcard->addrs[i].pbox, ";",
89 vcard->addrs[i].ext_addr, ";",
90 vcard->addrs[i].street_addr, ";",
91 vcard->addrs[i].city, ";",
92 vcard->addrs[i].state, ";",
93 vcard->addrs[i].code, ";",
94 vcard->addrs[i].country, "\n",
97 for (i = 0; i < vcard->num_tels; i++) {
98 silc_buffer_strformat(&buffer,
100 vcard->tels[i].type, ":",
101 vcard->tels[i].tel, "\n",
104 for (i = 0; i < vcard->num_emails; i++) {
105 silc_buffer_strformat(&buffer,
107 vcard->emails[i].type, ":",
108 vcard->emails[i].address, "\n",
112 silc_buffer_strformat(&buffer,
113 "NOTE:", vcard->note, "\n",
116 silc_buffer_strformat(&buffer,
117 "REV:", vcard->rev, "\n",
120 silc_buffer_strformat(&buffer, VCARD_FOOTER, SILC_STR_END);
123 *vcard_len = buffer.truelen;
129 #define VCARD_TOKEN(x) \
131 (x) = silc_memdup(val + off, i - off); \
136 /* Take on TYPE= token and prepare for next token, accept the
137 type also without TYPE= as it is possible */
138 #define VCARD_TYPETOKEN(x) \
141 if (!strcasecmp(val + off, "TYPE=")) \
143 (x) = silc_memdup(val + off + tmpi, i - off - tmpi - 1); \
144 tmpi = off + tmpi + strlen((x)) + 1; \
149 /* Take last token */
150 #define VCARD_LASTTOKEN(x) \
153 (x) = silc_memdup(val + off, len - off); \
158 bool silc_vcard_decode(const unsigned char *data, SilcUInt32 data_len,
162 bool has_begin = FALSE, has_end = FALSE;
165 val = (unsigned char *)data;
168 for (i = (val - data); i < data_len; i++) {
169 if (data[i] == '\0' || data[i] == '\n') {
170 len = i - (val - data);
174 if (!len || len > data_len - (val - data))
177 if (!strncasecmp(val, VCARD_HEADER, strlen(VCARD_HEADER))) {
179 } else if (!strncasecmp(val, "FN:", 3)) {
180 if (vcard->full_name)
183 vcard->full_name = silc_memdup(val + 3, len - 3);
184 } else if (!strncasecmp(val, "N:", 2)) {
185 if (vcard->family_name)
189 for (i = off; i < len; i++)
191 VCARD_TOKEN(vcard->family_name);
192 VCARD_TOKEN(vcard->first_name);
193 VCARD_TOKEN(vcard->middle_names);
194 VCARD_TOKEN(vcard->prefix);
196 if (!vcard->family_name && !vcard->first_name) {
197 VCARD_LASTTOKEN(vcard->family_name);
200 if (!vcard->first_name) {
201 VCARD_LASTTOKEN(vcard->first_name);
203 VCARD_LASTTOKEN(vcard->suffix);
206 } else if (!strncasecmp(val, "NICKNAME:", 9)) {
210 vcard->nickname = silc_memdup(val + 9, len - 9);
211 } else if (!strncasecmp(val, "BDAY:", 5)) {
215 vcard->bday = silc_memdup(val + 5, len - 5);
216 } else if (!strncasecmp(val, "TITLE:", 6)) {
220 vcard->title = silc_memdup(val + 6, len - 6);
221 } else if (!strncasecmp(val, "ROLE:", 5)) {
225 vcard->role = silc_memdup(val + 5, len - 5);
226 } else if (!strncasecmp(val, "ORG:", 4)) {
231 for (i = off; i < len; i++) {
233 VCARD_TOKEN(vcard->org_name);
237 /* It's possible to have ORG without last ';', so check for it */
238 if (!vcard->org_name) {
239 VCARD_LASTTOKEN(vcard->org_name);
241 VCARD_LASTTOKEN(vcard->org_unit);
244 } else if (!strncasecmp(val, "CATEGORIES:", 11)) {
245 if (vcard->categories)
248 vcard->categories = silc_memdup(val + 11, len - 11);
249 } else if (!strncasecmp(val, "CLASS:", 6)) {
253 vcard->class = silc_memdup(val + 6, len - 6);
254 } else if (!strncasecmp(val, "URL:", 4)) {
258 vcard->url = silc_memdup(val + 4, len - 4);
259 } else if (!strncasecmp(val, "LABEL;", 6)) {
263 vcard->label = silc_memdup(val + 6, len - 6);
264 } else if (!strncasecmp(val, "ADR;", 4)) {
265 vcard->addrs = silc_realloc(vcard->addrs, sizeof(*vcard->addrs) *
266 (vcard->num_addrs + 1));
267 memset(&vcard->addrs[vcard->num_addrs], 0, sizeof(*vcard->addrs));
270 for (i = off; i < len; i++)
272 VCARD_TYPETOKEN(vcard->addrs[vcard->num_addrs].type);
273 VCARD_TOKEN(vcard->addrs[vcard->num_addrs].pbox);
274 VCARD_TOKEN(vcard->addrs[vcard->num_addrs].ext_addr);
275 VCARD_TOKEN(vcard->addrs[vcard->num_addrs].street_addr);
276 VCARD_TOKEN(vcard->addrs[vcard->num_addrs].city);
277 VCARD_TOKEN(vcard->addrs[vcard->num_addrs].state);
278 VCARD_TOKEN(vcard->addrs[vcard->num_addrs].code);
280 VCARD_LASTTOKEN(vcard->addrs[vcard->num_addrs].country);
283 } else if (!strncasecmp(val, "TEL;", 4)) {
284 vcard->tels = silc_realloc(vcard->tels, sizeof(*vcard->tels) *
285 (vcard->num_tels + 1));
286 memset(&vcard->tels[vcard->num_tels], 0, sizeof(*vcard->tels));
289 for (i = off; i < len; i++)
292 VCARD_TYPETOKEN(vcard->tels[vcard->num_tels].type);
295 VCARD_LASTTOKEN(vcard->tels[vcard->num_tels].tel);
298 } else if (!strncasecmp(val, "EMAIL;", 6)) {
299 vcard->emails = silc_realloc(vcard->emails, sizeof(*vcard->emails) *
300 (vcard->num_emails + 1));
301 memset(&vcard->emails[vcard->num_emails], 0, sizeof(*vcard->emails));
304 for (i = off; i < len; i++)
307 VCARD_TYPETOKEN(vcard->emails[vcard->num_emails].type);
310 VCARD_LASTTOKEN(vcard->emails[vcard->num_emails].address);
313 } else if (!strncasecmp(val, "NOTE:", 5)) {
317 vcard->note = silc_memdup(val + 5, len - 5);
318 } else if (!strncasecmp(val, "REV:", 4)) {
322 vcard->rev = silc_memdup(val + 4, len - 4);
323 } else if (!strncasecmp(val, VCARD_FOOTER, strlen(VCARD_FOOTER))) {
328 val = strchr(val, '\n');
336 if (!has_begin || !has_end) {
337 silc_vcard_free(vcard);
344 /* Allocate vcard context */
346 SilcVCard silc_vcard_alloc(void)
348 SilcVCard vcard = silc_calloc(1, sizeof(*vcard));
351 vcard->dynamic = TRUE;
355 /* Free the vcard structure */
357 void silc_vcard_free(SilcVCard vcard)
361 silc_free(vcard->full_name);
362 silc_free(vcard->family_name);
363 silc_free(vcard->first_name);
364 silc_free(vcard->middle_names);
365 silc_free(vcard->prefix);
366 silc_free(vcard->suffix);
367 silc_free(vcard->nickname);
368 silc_free(vcard->bday);
369 silc_free(vcard->title);
370 silc_free(vcard->role);
371 silc_free(vcard->org_name);
372 silc_free(vcard->org_unit);
373 silc_free(vcard->categories);
374 silc_free(vcard->class);
375 silc_free(vcard->url);
376 silc_free(vcard->label);
377 for (i = 0; i < vcard->num_addrs; i++) {
378 silc_free(vcard->addrs[i].type);
379 silc_free(vcard->addrs[i].pbox);
380 silc_free(vcard->addrs[i].ext_addr);
381 silc_free(vcard->addrs[i].street_addr);
382 silc_free(vcard->addrs[i].city);
383 silc_free(vcard->addrs[i].state);
384 silc_free(vcard->addrs[i].code);
385 silc_free(vcard->addrs[i].country);
387 silc_free(vcard->addrs);
388 for (i = 0; i < vcard->num_tels; i++) {
389 silc_free(vcard->tels[i].type);
390 silc_free(vcard->tels[i].tel);
392 silc_free(vcard->tels);
393 for (i = 0; i < vcard->num_emails; i++) {
394 silc_free(vcard->emails[i].type);
395 silc_free(vcard->emails[i].address);
397 silc_free(vcard->emails);
398 silc_free(vcard->note);
399 silc_free(vcard->rev);
401 memset(vcard, 0, sizeof(*vcard));
403 if (vcard->dynamic) {
404 memset(vcard, 0, sizeof(*vcard));
409 /* Print card to file stream */
411 void silc_vcard_fprintf(SilcVCard vcard, FILE *stream)
414 fprintf(stream, "%s", VCARD_HEADER);
415 fprintf(stream, "%s", VCARD_VERSION);
416 if (vcard->full_name)
417 fprintf(stream, "FN:%s\n", vcard->full_name);
418 if (vcard->family_name)
419 fprintf(stream, "N:%s;%s;%s;%s;%s\n",
421 vcard->first_name ? vcard->first_name : "",
422 vcard->middle_names ? vcard->middle_names : "",
423 vcard->prefix ? vcard->prefix : "",
424 vcard->suffix ? vcard->suffix : "");
426 fprintf(stream, "NICKNAME:%s\n", vcard->nickname);
428 fprintf(stream, "BDAY:%s\n", vcard->bday);
430 fprintf(stream, "TITLE:%s\n", vcard->title);
432 fprintf(stream, "ROLE:%s\n", vcard->role);
434 fprintf(stream, "ORG:%s;%s\n", vcard->org_name,
435 vcard->org_unit ? vcard->org_unit : "");
436 if (vcard->categories)
437 fprintf(stream, "CATEGORIES:%s\n", vcard->categories);
439 fprintf(stream, "CLASS:%s\n", vcard->class);
441 fprintf(stream, "URL:%s\n", vcard->url);
443 fprintf(stream, "LABEL;%s\n", vcard->label);
444 for (i = 0; i < vcard->num_addrs; i++) {
445 fprintf(stream, "ADR;TYPE=%s:%s;%s;%s;%s;%s;%s;%s\n",
446 vcard->addrs[i].type,
447 vcard->addrs[i].pbox ? vcard->addrs[i].pbox : "",
448 vcard->addrs[i].ext_addr ? vcard->addrs[i].ext_addr : "",
449 vcard->addrs[i].street_addr ? vcard->addrs[i].street_addr : "",
450 vcard->addrs[i].city ? vcard->addrs[i].city : "",
451 vcard->addrs[i].state ? vcard->addrs[i].state : "",
452 vcard->addrs[i].code ? vcard->addrs[i].code : "",
453 vcard->addrs[i].country ? vcard->addrs[i].country : "");
455 for (i = 0; i < vcard->num_tels; i++) {
456 fprintf(stream, "TEL;TYPE=%s:%s\n",
458 vcard->tels[i].tel ? vcard->tels[i].tel : "");
460 for (i = 0; i < vcard->num_emails; i++) {
461 fprintf(stream, "EMAIL;TYPE=%s:%s\n",
462 vcard->emails[i].type,
463 vcard->emails[i].address ? vcard->emails[i].address : "");
466 fprintf(stream, "NOTE:%s\n", vcard->note);
468 fprintf(stream, "REV:%s\n", vcard->rev);
469 fprintf(stream, "%s", VCARD_FOOTER);