1898516da0b34d0873b1ed9fdeb1dab258f148bb
[citadel.git] / citadel / vcard.c
1 /*
2  * $Id$
3  *
4  * vCard implementation for Citadel/UX
5  *
6  * Copyright (C) 1999 by Art Cancro
7  * This code is freely redistributable under the terms of the GNU General
8  * Public License.  All other rights reserved.
9  */
10
11
12 #include <stdlib.h>
13 #include <unistd.h>
14 #include <stdio.h>
15 #include <fcntl.h>
16 #include <signal.h>
17
18 #if TIME_WITH_SYS_TIME
19 # include <sys/time.h>
20 # include <time.h>
21 #else
22 # if HAVE_SYS_TIME_H
23 #  include <sys/time.h>
24 # else
25 #  include <time.h>
26 # endif
27 #endif
28
29 #include <ctype.h>
30 #include <string.h>
31 #include <errno.h>
32 #include <limits.h>
33
34 #include "citadel.h"
35 #include "server.h"
36 #include "support.h"
37 #include "vcard.h"
38
39 /* 
40  * Constructor (empty vCard)
41  */
42 struct vCard *vcard_new() {
43         struct vCard *v;
44
45         v = (struct vCard *) mallok(sizeof(struct vCard));
46         if (v == NULL) return v;
47
48         v->magic = CTDL_VCARD_MAGIC;
49         v->numprops = 0;
50         v->prop = NULL;
51
52         return v;
53 }
54
55
56 /*
57  * Constructor (supply serialized vCard)
58  */
59 struct vCard *vcard_load(char *vtext) {
60         struct vCard *v;
61         int valid = 0;
62         char *mycopy, *ptr;
63         char *namebuf, *valuebuf;
64         int i;
65         int colonpos, nlpos;
66
67         mycopy = strdoop(vtext);
68         if (mycopy == NULL) return NULL;
69
70         /* First, fix this big pile o' vCard to make it more parseable.
71          * To make it easier to parse, we convert CRLF to LF, and unfold any
72          * multi-line fields into single lines.
73          */
74         for (i=0; i<strlen(mycopy); ++i) {
75                 if (!strncmp(&mycopy[i], "\r\n", 2)) {
76                         strcpy(&mycopy[i], &mycopy[i+1]);
77                 }
78                 if ( (mycopy[i]=='\n') && (isspace(mycopy[i+1])) ) {
79                         strcpy(&mycopy[i], &mycopy[i+1]);
80                 }
81         }
82
83         v = vcard_new();
84         if (v == NULL) return v;
85
86         ptr = mycopy;
87         while (strlen(ptr)>0) {
88                 colonpos = (-1);
89                 nlpos = (-1);
90                 colonpos = pattern2(ptr, ":");
91                 nlpos = pattern2(ptr, "\n");
92
93                 if ((nlpos > colonpos) && (colonpos > 0)) {
94                         namebuf = mallok(colonpos + 1);
95                         valuebuf = mallok(nlpos - colonpos + 1);
96                         strncpy(namebuf, ptr, colonpos);
97                         namebuf[colonpos] = 0;
98                         strncpy(valuebuf, &ptr[colonpos+1], nlpos-colonpos-1);
99                         valuebuf[nlpos-colonpos-1] = 0;
100
101                         if ( (!strcasecmp(namebuf, "end"))
102                            && (!strcasecmp(valuebuf, "vcard")) )  valid = 0;
103                         if ( (!strcasecmp(namebuf, "begin"))
104                            && (!strcasecmp(valuebuf, "vcard")) )  valid = 1;
105
106                         if ( (valid) && (strcasecmp(namebuf, "begin")) ) {
107                                 ++v->numprops;
108                                 v->prop = reallok(v->prop,
109                                         (v->numprops * sizeof(char *) * 2) );
110                                 v->prop[v->numprops-1].name = namebuf;
111                                 v->prop[v->numprops-1].value = valuebuf;
112                         } 
113                         else {
114                                 phree(namebuf);
115                                 phree(valuebuf);
116                         }
117
118                 }
119
120                 while ( (*ptr != '\n') && (strlen(ptr)>0) ) {
121                         ++ptr;
122                 }
123                 if (*ptr == '\n') ++ptr;
124         }
125
126         phree(mycopy);
127         return v;
128 }
129
130
131 /*
132  * Fetch the value of a particular key.
133  * If is_partial is set to 1, a partial match is ok (for example,
134  * a key of "tel;home" will satisfy a search for "tel").
135  * Set "instance" to a value higher than 0 to return subsequent instances
136  * of the same key.
137  * Set "get_propname" to nonzero to fetch the property name instead of value.
138  */
139 char *vcard_get_prop(struct vCard *v, char *propname,
140                         int is_partial, int instance, int get_propname) {
141         int i;
142         int found_instance = 0;
143
144         if (v->numprops) for (i=0; i<(v->numprops); ++i) {
145                 if ( (!strcasecmp(v->prop[i].name, propname))
146                    || (propname[0] == 0)
147                    || (  (!strncasecmp(v->prop[i].name,
148                                         propname, strlen(propname)))
149                          && (v->prop[i].name[strlen(propname)] == ';')
150                          && (is_partial) ) ) {
151                         if (instance == found_instance++) {
152                                 if (get_propname) {
153                                         return(v->prop[i].name);
154                                 }
155                                 else {
156                                         return(v->prop[i].value);
157                                 }
158                         }
159                 }
160         }
161
162         return NULL;
163 }
164
165
166
167
168 /*
169  * Destructor
170  */
171 void vcard_free(struct vCard *v) {
172         int i;
173         
174         if (v->magic != CTDL_VCARD_MAGIC) return;       /* Self-check */
175         
176         if (v->numprops) for (i=0; i<(v->numprops); ++i) {
177                 phree(v->prop[i].name);
178                 phree(v->prop[i].value);
179         }
180
181         if (v->prop != NULL) phree(v->prop);
182         
183         memset(v, 0, sizeof(struct vCard));
184         phree(v);
185 }
186
187
188
189
190 /*
191  * Set a name/value pair in the card
192  */
193 void vcard_set_prop(struct vCard *v, char *name, char *value, int append) {
194         int i;
195
196         if (v->magic != CTDL_VCARD_MAGIC) return;       /* Self-check */
197
198         /* If this key is already present, replace it */
199         if (!append) if (v->numprops) for (i=0; i<(v->numprops); ++i) {
200                 if (!strcasecmp(v->prop[i].name, name)) {
201                         phree(v->prop[i].name);
202                         phree(v->prop[i].value);
203                         v->prop[i].name = strdoop(name);
204                         v->prop[i].value = strdoop(value);
205                         return;
206                 }
207         }
208
209         /* Otherwise, append it */
210         ++v->numprops;
211         v->prop = reallok(v->prop,
212                 (v->numprops * sizeof(char *) * 2) );
213         v->prop[v->numprops-1].name = strdoop(name);
214         v->prop[v->numprops-1].value = strdoop(value);
215 }
216
217
218
219
220 /*
221  * Serialize a struct vcard into a standard text/x-vcard MIME type.
222  *
223  */
224 char *vcard_serialize(struct vCard *v)
225 {
226         char *ser;
227         int i;
228         size_t len;
229
230         if (v->magic != CTDL_VCARD_MAGIC) return NULL;  /* self check */
231
232         /* Figure out how big a buffer we need to allocate */
233         len = 64;       /* for begin, end, and a little padding for safety */
234         if (v->numprops) for (i=0; i<(v->numprops); ++i) {
235                 len = len +
236                         strlen(v->prop[i].name) +
237                         strlen(v->prop[i].value) + 4;
238         }
239
240         ser = mallok(len);
241         if (ser == NULL) return NULL;
242
243         strcpy(ser, "begin:vcard\r\n");
244         if (v->numprops) for (i=0; i<(v->numprops); ++i) {
245                 strcat(ser, v->prop[i].name);
246                 strcat(ser, ":");
247                 strcat(ser, v->prop[i].value);
248                 strcat(ser, "\r\n");
249         }
250         strcat(ser, "end:vcard\r\n");
251
252         return ser;
253 }