Comments.
[silc.git] / lib / silcutil / silcvcard.c
1 /*
2
3   silcvcard.c 
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 2002 Pekka Riikonen
8
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.
12
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.
17
18 */
19 /* $Id$ */
20 /* Implementation of the VCard (RFC 2426) */
21
22 #include "silcincludes.h"
23
24 #define VCARD_HEADER "BEGIN:VCARD\n"
25 #define VCARD_VERSION "VERSION:3.0\n"
26 #define VCARD_FOOTER "END:VCARD"
27
28 /* Free all data inside the card structure */
29
30 static void silc_vcard_free_internal(SilcVCard vcard)
31 {
32   int i;
33
34   silc_free(vcard->full_name);
35   silc_free(vcard->family_name);
36   silc_free(vcard->first_name);
37   silc_free(vcard->middle_names);
38   silc_free(vcard->prefix);
39   silc_free(vcard->suffix);
40   silc_free(vcard->nickname);
41   silc_free(vcard->bday);
42   silc_free(vcard->title);
43   silc_free(vcard->role);
44   silc_free(vcard->org_name);
45   silc_free(vcard->org_unit);
46   silc_free(vcard->categories);
47   silc_free(vcard->class);
48   silc_free(vcard->url);
49   silc_free(vcard->label);
50   for (i = 0; i < vcard->num_addrs; i++) {
51     silc_free(vcard->addrs[i].type);
52     silc_free(vcard->addrs[i].pbox);
53     silc_free(vcard->addrs[i].ext_addr);
54     silc_free(vcard->addrs[i].street_addr);
55     silc_free(vcard->addrs[i].city);
56     silc_free(vcard->addrs[i].state);
57     silc_free(vcard->addrs[i].code);
58     silc_free(vcard->addrs[i].country);
59   }
60   silc_free(vcard->addrs);
61   for (i = 0; i < vcard->num_tels; i++) {
62     silc_free(vcard->tels[i].type);
63     silc_free(vcard->tels[i].tel);
64   }
65   silc_free(vcard->tels);
66   for (i = 0; i < vcard->num_emails; i++) {
67     silc_free(vcard->emails[i].type);
68     silc_free(vcard->emails[i].address);
69   }
70   silc_free(vcard->emails);
71   silc_free(vcard->note);
72   silc_free(vcard->rev);
73 }
74
75 /* Encode VCard */
76
77 unsigned char *silc_vcard_encode(SilcVCard vcard, SilcUInt32 *vcard_len)
78 {
79   SilcBufferStruct buffer;
80   int i;
81
82   if (!vcard->full_name || !vcard->family_name || !vcard->first_name)
83     return NULL;
84
85   memset(&buffer, 0, sizeof(buffer));
86   silc_buffer_strformat(
87        &buffer,
88        VCARD_HEADER,
89        VCARD_VERSION,
90        "FN:", vcard->full_name, "\n",
91        "N:", vcard->family_name, ";", vcard->first_name, ";",
92        vcard->middle_names, ";", vcard->prefix, ";", vcard->suffix, "\n",
93        SILC_STR_END);
94
95   if (vcard->nickname)
96     silc_buffer_strformat(&buffer,
97                           "NICKNAME:", vcard->nickname, "\n",
98                           SILC_STR_END);
99   if (vcard->bday)
100     silc_buffer_strformat(&buffer,
101                           "BDAY:", vcard->bday, "\n",
102                           SILC_STR_END);
103   if (vcard->title)
104     silc_buffer_strformat(&buffer,
105                           "TITLE:", vcard->title, "\n",
106                           SILC_STR_END);
107   if (vcard->role)
108     silc_buffer_strformat(&buffer,
109                           "ROLE:", vcard->role, "\n",
110                           SILC_STR_END);
111   if (vcard->org_name)
112     silc_buffer_strformat(&buffer,
113                           "ORG:", vcard->org_name, ";", vcard->org_unit, "\n",
114                           SILC_STR_END);
115   if (vcard->categories)
116     silc_buffer_strformat(&buffer,
117                           "CATEGORIES:", vcard->categories, "\n",
118                           SILC_STR_END);
119   if (vcard->class)
120     silc_buffer_strformat(&buffer,
121                           "CLASS:", vcard->class, "\n",
122                           SILC_STR_END);
123   if (vcard->url)
124     silc_buffer_strformat(&buffer,
125                           "URL:", vcard->url, "\n",
126                           SILC_STR_END);
127   if (vcard->label)
128     silc_buffer_strformat(&buffer,
129                           "LABEL;", vcard->url, "\n",
130                           SILC_STR_END);
131   for (i = 0; i < vcard->num_addrs; i++) {
132     silc_buffer_strformat(&buffer,
133                           "ADR;TYPE=",
134                           vcard->addrs[i].type, ":",
135                           vcard->addrs[i].pbox, ";",
136                           vcard->addrs[i].ext_addr, ";",
137                           vcard->addrs[i].street_addr, ";",
138                           vcard->addrs[i].city, ";",
139                           vcard->addrs[i].state, ";",
140                           vcard->addrs[i].code, ";",
141                           vcard->addrs[i].country, "\n",
142                           SILC_STR_END);
143   }
144   for (i = 0; i < vcard->num_tels; i++) {
145     silc_buffer_strformat(&buffer,
146                           "TEL;TYPE=",
147                           vcard->tels[i].type, ":",
148                           vcard->tels[i].tel, "\n",
149                           SILC_STR_END);
150   }
151   for (i = 0; i < vcard->num_emails; i++) {
152     silc_buffer_strformat(&buffer,
153                           "EMAIL;TYPE=",
154                           vcard->emails[i].type, ":",
155                           vcard->emails[i].address, "\n",
156                           SILC_STR_END);
157   }
158   if (vcard->note)
159     silc_buffer_strformat(&buffer,
160                           "NOTE:", vcard->note, "\n",
161                           SILC_STR_END);
162   if (vcard->rev)
163     silc_buffer_strformat(&buffer,
164                           "REV:", vcard->rev, "\n",
165                           SILC_STR_END);
166
167   silc_buffer_strformat(&buffer, VCARD_FOOTER, SILC_STR_END);
168
169   if (vcard_len)
170     *vcard_len = buffer.truelen;
171
172   return buffer.head;
173 }
174
175 /* Take one token */
176 #define VCARD_TOKEN(x)                          \
177   if (!(x)) {                                   \
178     (x) = silc_memdup(val + off, i - off);      \
179     off = i + 1;                                \
180     continue;                                   \
181   }
182
183 /* Take on TYPE= token and prepare for next token */
184 #define VCARD_TYPETOKEN(x)                              \
185   if (!(x)) {                                           \
186     int tmpi;                                           \
187     (x) = silc_memdup(val + off + 5, i - off - 5 - 1);  \
188     tmpi = off + 5 + strlen((x)) + 1;                   \
189     off = i;                                            \
190     i = tmpi;                                           \
191   }
192
193 /* Take last token */
194 #define VCARD_LASTTOKEN(x)                      \
195   if (!(x)) {                                   \
196     if (off < len)                              \
197       (x) = silc_memdup(val + off, len - off);  \
198   }                                             \
199
200 /* Decode VCard */
201
202 bool silc_vcard_decode(const unsigned char *data, SilcUInt32 data_len,
203                        SilcVCard vcard)
204 {
205   unsigned char *val;
206   bool has_begin = FALSE, has_end = FALSE;
207   int len, i, off = 0;
208   
209   val = (unsigned char *)data;
210   while (val) {
211     if (!strchr(val, '\n'))
212       break;
213     len = strchr(val, '\n') - (char *)val;
214     if (len > data_len - (val - data))
215       break;
216
217     if (!strncasecmp(val, VCARD_HEADER, strlen(VCARD_HEADER))) {
218       has_begin = TRUE;
219     } else if (!strncasecmp(val, "FN:", 3)) {
220       if (vcard->full_name)
221         break;
222       if (len - 3)
223         vcard->full_name = silc_memdup(val + 3, len - 3);
224     } else if (!strncasecmp(val, "N:", 2)) {
225       if (vcard->family_name)
226         break;
227       if (len - 2) {
228         off = 2;
229         for (i = off; i < len; i++)
230           if (val[i] == ';') {
231             VCARD_TOKEN(vcard->family_name);
232             VCARD_TOKEN(vcard->first_name);
233             VCARD_TOKEN(vcard->middle_names);
234             VCARD_TOKEN(vcard->prefix);
235           }
236         VCARD_LASTTOKEN(vcard->suffix);
237       }
238     } else if (!strncasecmp(val, "NICKNAME:", 9)) {
239       if (vcard->nickname)
240         continue;
241       if (len - 9)
242         vcard->nickname = silc_memdup(val + 9, len - 9);
243     } else if (!strncasecmp(val, "BDAY:", 5)) {
244       if (vcard->bday)
245         continue;
246       if (len - 5)
247         vcard->bday = silc_memdup(val + 5, len - 5);
248     } else if (!strncasecmp(val, "TITLE:", 6)) {
249       if (vcard->title)
250         continue;
251       if (len - 6)
252         vcard->title = silc_memdup(val + 6, len - 6);
253     } else if (!strncasecmp(val, "ROLE:", 5)) {
254       if (vcard->role)
255         continue;
256       if (len - 5)
257         vcard->role = silc_memdup(val + 5, len - 5);
258     } else if (!strncasecmp(val, "ORG:", 4)) {
259       if (vcard->org_name)
260         continue;
261       if (len - 4) {
262         off = 4;
263         for (i = off; i < len; i++) {
264           if (val[i] == ';') {
265             VCARD_TOKEN(vcard->org_name);
266             break;
267           }
268         }
269         /* It's possible to have ORG without last ';', so check for it */
270         if (!vcard->org_name) {
271           VCARD_LASTTOKEN(vcard->org_name);
272         } else {
273           VCARD_LASTTOKEN(vcard->org_unit);
274         }
275       }
276     } else if (!strncasecmp(val, "CATEGORIES:", 11)) {
277       if (vcard->categories)
278         continue;
279       if (len - 11)
280         vcard->categories = silc_memdup(val + 11, len - 11);
281     } else if (!strncasecmp(val, "CLASS:", 6)) {
282       if (vcard->class)
283         continue;
284       if (len - 6)
285         vcard->class = silc_memdup(val + 6, len - 6);
286     } else if (!strncasecmp(val, "URL:", 4)) {
287       if (vcard->url)
288         continue;
289       if (len - 4)
290         vcard->url = silc_memdup(val + 4, len - 4);
291     } else if (!strncasecmp(val, "LABEL;", 6)) {
292       if (vcard->label)
293         continue;
294       if (len - 6)
295         vcard->label = silc_memdup(val + 6, len - 6);
296     } else if (!strncasecmp(val, "ADR;", 4)) {
297       vcard->addrs = silc_realloc(vcard->addrs, sizeof(*vcard->addrs) *
298                                   (vcard->num_addrs + 1));
299       if (len - 4) {
300         off = 4;
301         for (i = off; i < len; i++)
302           if (val[i] == ';') {
303             VCARD_TYPETOKEN(vcard->addrs[vcard->num_addrs].type);
304             VCARD_TOKEN(vcard->addrs[vcard->num_addrs].pbox);
305             VCARD_TOKEN(vcard->addrs[vcard->num_addrs].ext_addr);
306             VCARD_TOKEN(vcard->addrs[vcard->num_addrs].street_addr);
307             VCARD_TOKEN(vcard->addrs[vcard->num_addrs].city);
308             VCARD_TOKEN(vcard->addrs[vcard->num_addrs].state);
309             VCARD_TOKEN(vcard->addrs[vcard->num_addrs].code);
310           }
311         VCARD_LASTTOKEN(vcard->addrs[vcard->num_addrs].country);
312       }
313       vcard->num_addrs++;
314     } else if (!strncasecmp(val, "TEL;", 4)) {
315       vcard->tels = silc_realloc(vcard->tels, sizeof(*vcard->tels) *
316                                  (vcard->num_tels + 1));
317       if (len - 4) {
318         off = 4;
319         for (i = off; i < len; i++)
320           if (val[i] == ':') {
321             i++;
322             VCARD_TYPETOKEN(vcard->tels[vcard->num_tels].type);
323             break;
324           }
325         VCARD_LASTTOKEN(vcard->tels[vcard->num_tels].tel);
326       }
327       vcard->num_tels++;
328     } else if (!strncasecmp(val, "EMAIL;", 6)) {
329       vcard->emails = silc_realloc(vcard->emails, sizeof(*vcard->emails) *
330                                    (vcard->num_emails + 1));
331       if (len - 6) {
332         off = 6;
333         for (i = off; i < len; i++)
334           if (val[i] == ':') {
335             i++;
336             VCARD_TYPETOKEN(vcard->emails[vcard->num_emails].type);
337             break;
338           }
339         VCARD_LASTTOKEN(vcard->emails[vcard->num_emails].address);
340       }
341       vcard->num_emails++;
342     } else if (!strncasecmp(val, "NOTE:", 5)) {
343       if (vcard->note)
344         continue;
345       if (len - 5)
346         vcard->note = silc_memdup(val + 5, len - 5);
347     } else if (!strncasecmp(val, "REV:", 4)) {
348       if (vcard->rev)
349         continue;
350       if (len - 4)
351         vcard->rev = silc_memdup(val + 4, len - 4);
352     } else if (!strncasecmp(val, VCARD_FOOTER, strlen(VCARD_FOOTER))) {
353       has_end = TRUE;
354       break;
355     }
356
357     val = strchr(val, '\n');
358     if (!val || !(*val))
359       break;
360     val++;
361     if (!val || !(*val))
362       break;
363   }
364
365   if (!has_begin || !has_end) {
366     silc_vcard_free_internal(vcard);
367     return FALSE;
368   }
369
370   return TRUE;
371 }
372
373 /* Free the vcard structure */
374
375 void silc_vcard_free(SilcVCard vcard)
376 {
377   silc_vcard_free_internal(vcard);
378   memset(vcard, 'F', sizeof(*vcard));
379   silc_free(vcard);
380 }
381
382 /* Print card to file stream */
383
384 void silc_vcard_fprintf(SilcVCard vcard, FILE *stream)
385 {
386   int i;
387   fprintf(stream, "%s", VCARD_HEADER);
388   fprintf(stream, "%s", VCARD_VERSION);
389   if (vcard->full_name)
390     fprintf(stream, "FN:%s\n", vcard->full_name);
391   if (vcard->family_name)
392     fprintf(stream, "N:%s;%s;%s;%s;%s\n",
393             vcard->family_name,
394             vcard->first_name ? vcard->first_name : "",
395             vcard->middle_names ? vcard->middle_names : "",
396             vcard->prefix ? vcard->prefix : "",
397             vcard->suffix ? vcard->suffix : "");
398   if (vcard->nickname)
399     fprintf(stream, "NICKNAME:%s\n", vcard->nickname);
400   if (vcard->bday)
401     fprintf(stream, "BDAY:%s\n", vcard->bday);
402   if (vcard->title)
403     fprintf(stream, "TITLE:%s\n", vcard->title);
404   if (vcard->role)
405     fprintf(stream, "ROLE:%s\n", vcard->role);
406   if (vcard->org_name)
407     fprintf(stream, "ORG:%s;%s\n", vcard->org_name,
408             vcard->org_unit ? vcard->org_unit : "");
409   if (vcard->categories)
410     fprintf(stream, "CATEGORIES:%s\n", vcard->categories);
411   if (vcard->class)
412     fprintf(stream, "CLASS:%s\n", vcard->class);
413   if (vcard->url)
414     fprintf(stream, "URL:%s\n", vcard->url);
415   if (vcard->label)
416     fprintf(stream, "LABEL;%s\n", vcard->label);
417   for (i = 0; i < vcard->num_addrs; i++) {
418     fprintf(stream, "ADR;TYPE=%s:%s;%s;%s;%s;%s;%s;%s\n",
419             vcard->addrs[i].type,
420             vcard->addrs[i].pbox ? vcard->addrs[i].pbox : "",
421             vcard->addrs[i].ext_addr ? vcard->addrs[i].ext_addr : "",
422             vcard->addrs[i].street_addr ? vcard->addrs[i].street_addr : "",
423             vcard->addrs[i].city ? vcard->addrs[i].city : "",
424             vcard->addrs[i].state ? vcard->addrs[i].state : "",
425             vcard->addrs[i].code ? vcard->addrs[i].code : "",
426             vcard->addrs[i].country ? vcard->addrs[i].country : "");
427   }
428   for (i = 0; i < vcard->num_tels; i++) {
429     fprintf(stream, "TEL;TYPE=%s:%s\n",
430             vcard->tels[i].type,
431             vcard->tels[i].tel ? vcard->tels[i].tel : "");
432   }
433   for (i = 0; i < vcard->num_emails; i++) {
434     fprintf(stream, "EMAIL;TYPE=%s:%s\n",
435             vcard->emails[i].type,
436             vcard->emails[i].address ? vcard->emails[i].address : "");
437   }
438   if (vcard->note)
439     fprintf(stream, "NOTE:%s\n", vcard->note);
440   if (vcard->rev)
441     fprintf(stream, "REV:%s\n", vcard->rev);
442   fprintf(stream, "%s", VCARD_FOOTER);
443   fflush(stream);
444 }