Merged from silc_1_0_branch.
[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->catclass)
73     silc_buffer_strformat(&buffer,
74                           "CLASS:", vcard->catclass, "\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].telnum, "\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, accept the
137    type also without TYPE= as it is possible */
138 #define VCARD_TYPETOKEN(x)                                      \
139   if (!(x)) {                                                   \
140     int tmpi = 0;                                               \
141     if (!strncasecmp(val + off, "TYPE=", 5))                    \
142       tmpi = 5;                                                 \
143     (x) = silc_memdup(val + off + tmpi, i - off - tmpi - 1);    \
144     tmpi = off + tmpi + strlen((x)) + 1;                        \
145     off = i;                                                    \
146     i = tmpi;                                                   \
147   }
148
149 /* Take last token */
150 #define VCARD_LASTTOKEN(x)                      \
151   if (!(x)) {                                   \
152     if (off < len)                              \
153       (x) = silc_memdup(val + off, len - off);  \
154   }                                             \
155
156 /* Get one (single) field */
157 #define VCARD_FIELD(val, c, x)                          \
158 do {                                                    \
159   if (!strncasecmp(val, (c), strlen((c)))) {            \
160     int tmpl = strlen((c));                             \
161     if ((x))                                            \
162       break;                                            \
163     if (len - tmpl > 0)                                 \
164       (x) = silc_memdup(val + tmpl, len - tmpl);        \
165     goto next;                                          \
166   }                                                     \
167 } while(0)
168
169 /* Decode VCard */
170
171 bool silc_vcard_decode(const unsigned char *data, SilcUInt32 data_len,
172                        SilcVCard vcard)
173 {
174   unsigned char *val;
175   bool has_begin = FALSE, has_end = FALSE;
176   int len, i, off = 0;
177   
178   val = (unsigned char *)data;
179   while (val) {
180     len = 0;
181     for (i = (val - data); i < data_len; i++) {
182       if (data[i] == '\0' || data[i] == '\n') {
183         len = i - (val - data);
184         break;
185       }
186     }
187     if (!len || len > data_len - (val - data))
188       break;
189
190     /* Check for mandatory header and footer */
191     if (!strncasecmp(val, VCARD_HEADER, strlen(VCARD_HEADER))) {
192       has_begin = TRUE;
193       goto next;
194     }
195     if (!strncasecmp(val, VCARD_FOOTER, strlen(VCARD_FOOTER))) {
196       has_end = TRUE;
197       goto next;
198     }
199
200     /* Get single fields */
201     VCARD_FIELD(val, "FN:", vcard->full_name);
202     VCARD_FIELD(val, "NICKNAME:", vcard->nickname);
203     VCARD_FIELD(val, "BDAY:", vcard->bday);
204     VCARD_FIELD(val, "TITLE:", vcard->title);
205     VCARD_FIELD(val, "ROLE:", vcard->role);
206     VCARD_FIELD(val, "CATEGORIES:", vcard->categories);
207     VCARD_FIELD(val, "CLASS:", vcard->catclass);
208     VCARD_FIELD(val, "URL:", vcard->url);
209     VCARD_FIELD(val, "LABEL;", vcard->label);
210     VCARD_FIELD(val, "NOTE:", vcard->note);
211     VCARD_FIELD(val, "REV:", vcard->rev);
212
213     /* Get multi-column fields */
214
215     if (!strncasecmp(val, "N:", 2)) {
216       if (vcard->family_name)
217         break;
218       if (len - 2) {
219         off = 2;
220         for (i = off; i < len; i++)
221           if (val[i] == ';') {
222             VCARD_TOKEN(vcard->family_name);
223             VCARD_TOKEN(vcard->first_name);
224             VCARD_TOKEN(vcard->middle_names);
225             VCARD_TOKEN(vcard->prefix);
226           }
227         if (!vcard->family_name && !vcard->first_name) {
228           VCARD_LASTTOKEN(vcard->family_name);
229           off += (len - off);
230         }
231         if (!vcard->first_name) {
232           VCARD_LASTTOKEN(vcard->first_name);
233         } else {
234           VCARD_LASTTOKEN(vcard->suffix);
235         }
236       }
237       goto next;
238     }
239
240     if (!strncasecmp(val, "ORG:", 4)) {
241       if (vcard->org_name)
242         continue;
243       if (len - 4) {
244         off = 4;
245         for (i = off; i < len; i++) {
246           if (val[i] == ';') {
247             VCARD_TOKEN(vcard->org_name);
248             break;
249           }
250         }
251         /* It's possible to have ORG without last ';', so check for it */
252         if (!vcard->org_name) {
253           VCARD_LASTTOKEN(vcard->org_name);
254         } else {
255           VCARD_LASTTOKEN(vcard->org_unit);
256         }
257       }
258       goto next;
259     }
260
261     if (!strncasecmp(val, "ADR;", 4)) {
262       vcard->addrs = silc_realloc(vcard->addrs, sizeof(*vcard->addrs) *
263                                   (vcard->num_addrs + 1));
264       memset(&vcard->addrs[vcard->num_addrs], 0, sizeof(*vcard->addrs));
265       if (len - 4) {
266         off = 4;
267         for (i = off; i < len; i++)
268           if (val[i] == ';') {
269             VCARD_TYPETOKEN(vcard->addrs[vcard->num_addrs].type);
270             VCARD_TOKEN(vcard->addrs[vcard->num_addrs].pbox);
271             VCARD_TOKEN(vcard->addrs[vcard->num_addrs].ext_addr);
272             VCARD_TOKEN(vcard->addrs[vcard->num_addrs].street_addr);
273             VCARD_TOKEN(vcard->addrs[vcard->num_addrs].city);
274             VCARD_TOKEN(vcard->addrs[vcard->num_addrs].state);
275             VCARD_TOKEN(vcard->addrs[vcard->num_addrs].code);
276           }
277         VCARD_LASTTOKEN(vcard->addrs[vcard->num_addrs].country);
278       }
279       vcard->num_addrs++;
280       goto next;
281     }
282
283     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));
287       if (len - 4) {
288         off = 4;
289         for (i = off; i < len; i++)
290           if (val[i] == ':') {
291             i++;
292             VCARD_TYPETOKEN(vcard->tels[vcard->num_tels].type);
293             break;
294           }
295         VCARD_LASTTOKEN(vcard->tels[vcard->num_tels].telnum);
296       }
297       vcard->num_tels++;
298       goto next;
299     }
300
301     if (!strncasecmp(val, "EMAIL;", 6)) {
302       vcard->emails = silc_realloc(vcard->emails, sizeof(*vcard->emails) *
303                                    (vcard->num_emails + 1));
304       memset(&vcard->emails[vcard->num_emails], 0, sizeof(*vcard->emails));
305       if (len - 6) {
306         off = 6;
307         for (i = off; i < len; i++)
308           if (val[i] == ':') {
309             i++;
310             VCARD_TYPETOKEN(vcard->emails[vcard->num_emails].type);
311             break;
312           }
313         VCARD_LASTTOKEN(vcard->emails[vcard->num_emails].address);
314       }
315       vcard->num_emails++;
316       goto next;
317     }
318
319   next:
320     val = strchr(val, '\n');
321     if (!val)
322       break;
323     val++;
324     if (!val || !(*val))
325       break;
326   }
327
328   if (!has_begin || !has_end || !vcard->full_name) {
329     silc_vcard_free(vcard);
330     return FALSE;
331   }
332
333   return TRUE;
334 }
335
336 /* Allocate vcard context */
337
338 SilcVCard silc_vcard_alloc(void)
339 {
340   SilcVCard vcard = silc_calloc(1, sizeof(*vcard));
341   if (!vcard)
342     return NULL;
343   vcard->dynamic = TRUE;
344   return vcard;
345 }
346
347 /* Free the vcard structure */
348
349 void silc_vcard_free(SilcVCard vcard)
350 {
351   int i;
352
353   silc_free(vcard->full_name);
354   silc_free(vcard->family_name);
355   silc_free(vcard->first_name);
356   silc_free(vcard->middle_names);
357   silc_free(vcard->prefix);
358   silc_free(vcard->suffix);
359   silc_free(vcard->nickname);
360   silc_free(vcard->bday);
361   silc_free(vcard->title);
362   silc_free(vcard->role);
363   silc_free(vcard->org_name);
364   silc_free(vcard->org_unit);
365   silc_free(vcard->categories);
366   silc_free(vcard->catclass);
367   silc_free(vcard->url);
368   silc_free(vcard->label);
369   for (i = 0; i < vcard->num_addrs; i++) {
370     silc_free(vcard->addrs[i].type);
371     silc_free(vcard->addrs[i].pbox);
372     silc_free(vcard->addrs[i].ext_addr);
373     silc_free(vcard->addrs[i].street_addr);
374     silc_free(vcard->addrs[i].city);
375     silc_free(vcard->addrs[i].state);
376     silc_free(vcard->addrs[i].code);
377     silc_free(vcard->addrs[i].country);
378   }
379   silc_free(vcard->addrs);
380   for (i = 0; i < vcard->num_tels; i++) {
381     silc_free(vcard->tels[i].type);
382     silc_free(vcard->tels[i].telnum);
383   }
384   silc_free(vcard->tels);
385   for (i = 0; i < vcard->num_emails; i++) {
386     silc_free(vcard->emails[i].type);
387     silc_free(vcard->emails[i].address);
388   }
389   silc_free(vcard->emails);
390   silc_free(vcard->note);
391   silc_free(vcard->rev);
392   if (!vcard->dynamic)
393     memset(vcard, 0, sizeof(*vcard));
394
395   if (vcard->dynamic) {
396     memset(vcard, 0, sizeof(*vcard));
397     silc_free(vcard);
398   }
399 }
400
401 /* Print card to file stream */
402
403 void silc_vcard_fprintf(SilcVCard vcard, FILE *stream)
404 {
405   int i;
406   fprintf(stream, "%s", VCARD_HEADER);
407   fprintf(stream, "%s", VCARD_VERSION);
408   if (vcard->full_name)
409     fprintf(stream, "FN:%s\n", vcard->full_name);
410   if (vcard->family_name)
411     fprintf(stream, "N:%s;%s;%s;%s;%s\n",
412             vcard->family_name,
413             vcard->first_name ? vcard->first_name : "",
414             vcard->middle_names ? vcard->middle_names : "",
415             vcard->prefix ? vcard->prefix : "",
416             vcard->suffix ? vcard->suffix : "");
417   if (vcard->nickname)
418     fprintf(stream, "NICKNAME:%s\n", vcard->nickname);
419   if (vcard->bday)
420     fprintf(stream, "BDAY:%s\n", vcard->bday);
421   if (vcard->title)
422     fprintf(stream, "TITLE:%s\n", vcard->title);
423   if (vcard->role)
424     fprintf(stream, "ROLE:%s\n", vcard->role);
425   if (vcard->org_name)
426     fprintf(stream, "ORG:%s;%s\n", vcard->org_name,
427             vcard->org_unit ? vcard->org_unit : "");
428   if (vcard->categories)
429     fprintf(stream, "CATEGORIES:%s\n", vcard->categories);
430   if (vcard->catclass)
431     fprintf(stream, "CLASS:%s\n", vcard->catclass);
432   if (vcard->url)
433     fprintf(stream, "URL:%s\n", vcard->url);
434   if (vcard->label)
435     fprintf(stream, "LABEL;%s\n", vcard->label);
436   for (i = 0; i < vcard->num_addrs; i++) {
437     fprintf(stream, "ADR;TYPE=%s:%s;%s;%s;%s;%s;%s;%s\n",
438             vcard->addrs[i].type,
439             vcard->addrs[i].pbox ? vcard->addrs[i].pbox : "",
440             vcard->addrs[i].ext_addr ? vcard->addrs[i].ext_addr : "",
441             vcard->addrs[i].street_addr ? vcard->addrs[i].street_addr : "",
442             vcard->addrs[i].city ? vcard->addrs[i].city : "",
443             vcard->addrs[i].state ? vcard->addrs[i].state : "",
444             vcard->addrs[i].code ? vcard->addrs[i].code : "",
445             vcard->addrs[i].country ? vcard->addrs[i].country : "");
446   }
447   for (i = 0; i < vcard->num_tels; i++) {
448     fprintf(stream, "TEL;TYPE=%s:%s\n",
449             vcard->tels[i].type,
450             vcard->tels[i].telnum ? vcard->tels[i].telnum : "");
451   }
452   for (i = 0; i < vcard->num_emails; i++) {
453     fprintf(stream, "EMAIL;TYPE=%s:%s\n",
454             vcard->emails[i].type,
455             vcard->emails[i].address ? vcard->emails[i].address : "");
456   }
457   if (vcard->note)
458     fprintf(stream, "NOTE:%s\n", vcard->note);
459   if (vcard->rev)
460     fprintf(stream, "REV:%s\n", vcard->rev);
461   fprintf(stream, "%s", VCARD_FOOTER);
462   fflush(stream);
463 }