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