65dfd1c0fc5cbd632eb0b1f0fac6c2ab2a5cb25e
[citadel.git] / webcit / vcard.c
1 /*
2  * $Id$
3  * Copyright (C) 1999-2006 by Art Cancro
4  * This code is freely redistributable under the terms of the GNU General
5  * Public License.  All other rights reserved.
6  */
7 /**
8  * \defgroup VCardMain vCard data type implementation for the Citadel system.
9  * \ingroup VCards
10  */
11 /*@{*/
12 #include "webcit.h"
13 #include "webserver.h"
14 #include "vcard.h"
15
16 /** 
17  * \brief Constructor (empty vCard)
18  * \return an empty vcard
19  */
20 struct vCard *vcard_new() {
21         struct vCard *v;
22
23         v = (struct vCard *) malloc(sizeof(struct vCard));
24         if (v == NULL) return v;
25
26         v->magic = CTDL_VCARD_MAGIC;
27         v->numprops = 0;
28         v->prop = NULL;
29
30         return v;
31 }
32
33 /**
34  * \brief       Remove the "charset=" attribute from a vCard property name
35  *
36  * \param       strbuf          The property name string to be stripped
37  */
38 void remove_charset_attribute(char *strbuf)
39 {
40         int i, t;
41         char compare[256];
42
43         t = num_tokens(strbuf, ';');
44         for (i=0; i<t; ++i) {
45                 extract_token(compare, strbuf, i, ';', sizeof compare);
46                 striplt(compare);
47                 if (!strncasecmp(compare, "charset=", 8)) {
48                         remove_token(strbuf, i, ';');
49                 }
50         }
51         if (strlen(strbuf) > 0) {
52                 if (strbuf[strlen(strbuf)-1] == ';') {
53                         strbuf[strlen(strbuf)-1] = 0;
54                 }
55         }
56 }
57
58
59 /*
60  * \brief       Add a property to a vCard
61  *
62  * \param       v               vCard structure to which we are adding
63  * \param       propname        name of new property
64  * \param       propvalue       value of new property
65  */
66 void vcard_add_prop(struct vCard *v, char *propname, char *propvalue) {
67         ++v->numprops;
68         v->prop = realloc(v->prop,
69                 (v->numprops * sizeof(struct vCardProp)) );
70         v->prop[v->numprops-1].name = strdup(propname);
71         v->prop[v->numprops-1].value = strdup(propvalue);
72 }
73
74
75
76 /**
77  * \brief Constructor (supply serialized vCard)
78  * \param vtext the text to parse into the new vcard
79  * \return the parsed VCard
80  */
81 struct vCard *vcard_load(char *vtext) {
82         struct vCard *v;
83         int valid = 0;
84         char *mycopy, *ptr;
85         char *namebuf, *valuebuf;
86         int i;
87         int len;
88         int colonpos, nlpos;
89
90         if (vtext == NULL) return vcard_new();
91         mycopy = strdup(vtext);
92         if (mycopy == NULL) return NULL;
93
94         /**
95          * First, fix this big pile o' vCard to make it more parseable.
96          * To make it easier to parse, we convert CRLF to LF, and unfold any
97          * multi-line fields into single lines.
98          */
99         len = strlen(mycopy);
100         for (i=0; i<len; ++i) {
101                 if (!strncmp(&mycopy[i], "\r\n", 2)) {
102                         memmove(&mycopy[i], &mycopy[i+1], len - i);
103                         len --;
104                 }
105                 if ( (mycopy[i]=='\n') && (isspace(mycopy[i+1])) ) {
106                         memmove(&mycopy[i], &mycopy[i+1], len - i);
107                         len --;
108                 }
109         }
110
111         v = vcard_new();
112         if (v == NULL) return v;
113
114         ptr = mycopy;
115         while (*ptr != '\0') {
116                 colonpos = (-1);
117                 nlpos = (-1);
118                 colonpos = pattern2(ptr, ":");
119                 nlpos = pattern2(ptr, "\n");
120
121                 if ((nlpos > colonpos) && (colonpos > 0)) {
122                         namebuf = malloc(colonpos + 1);
123                         valuebuf = malloc(nlpos - colonpos + 1);
124                         strncpy(namebuf, ptr, colonpos);
125                         namebuf[colonpos] = 0;
126                         strncpy(valuebuf, &ptr[colonpos+1], nlpos-colonpos-1);
127                         valuebuf[nlpos-colonpos-1] = 0;
128
129                         if (!strcasecmp(namebuf, "end")) {
130                                 valid = 0;
131                         }
132                         if (    (!strcasecmp(namebuf, "begin"))
133                                 && (!strcasecmp(valuebuf, "vcard"))
134                         ) {
135                                 valid = 1;
136                         }
137
138                         if ( (valid) && (strcasecmp(namebuf, "begin")) ) {
139                                 remove_charset_attribute(namebuf);
140                                 ++v->numprops;
141                                 v->prop = realloc(v->prop,
142                                         (v->numprops * sizeof(struct vCardProp))
143                                 );
144                                 v->prop[v->numprops-1].name = namebuf;
145                                 v->prop[v->numprops-1].value = valuebuf;
146                         } 
147                         else {
148                                 free(namebuf);
149                                 free(valuebuf);
150                         }
151
152                 }
153
154                 while ( (*ptr != '\n') && (*ptr != '\0') ) {
155                         ++ptr;
156                 }
157                 if (*ptr == '\n') ++ptr;
158         }
159
160         free(mycopy);
161         return v;
162 }
163
164
165 /**
166  * \brief Fetch the value of a particular key.
167  * If is_partial is set to 1, a partial match is ok (for example,
168  * a key of "tel;home" will satisfy a search for "tel").
169  * Set "instance" to a value higher than 0 to return subsequent instances
170  * of the same key.
171  * Set "get_propname" to nonzero to fetch the property name instead of value.
172  * \param v vCard to get keyvalue from
173  * \param propname key to retrieve
174  * \param is_partial dunno???
175  * \param instance if >0 return a later token of the value
176  * \param get_propname if nonzero get the real property name???
177  * \return the requested value / token / propertyname
178  */
179 char *vcard_get_prop(struct vCard *v, char *propname,
180                         int is_partial, int instance, int get_propname) {
181         int i;
182         int found_instance = 0;
183
184         if (v->numprops) for (i=0; i<(v->numprops); ++i) {
185                 if ( (!strcasecmp(v->prop[i].name, propname))
186                    || (propname[0] == 0)
187                    || (  (!strncasecmp(v->prop[i].name,
188                                         propname, strlen(propname)))
189                          && (v->prop[i].name[strlen(propname)] == ';')
190                          && (is_partial) ) ) {
191                         if (instance == found_instance++) {
192                                 if (get_propname) {
193                                         return(v->prop[i].name);
194                                 }
195                                 else {
196                                         return(v->prop[i].value);
197                                 }
198                         }
199                 }
200         }
201
202         return NULL;
203 }
204
205
206
207
208 /**
209  * \brief Destructor
210  * kill a vCard
211  * \param v the vCard to purge from memory
212  */
213 void vcard_free(struct vCard *v) {
214         int i;
215         
216         if (v->magic != CTDL_VCARD_MAGIC) return;       /* Self-check */
217         
218         if (v->numprops) for (i=0; i<(v->numprops); ++i) {
219                 free(v->prop[i].name);
220                 free(v->prop[i].value);
221         }
222
223         if (v->prop != NULL) free(v->prop);
224         
225         memset(v, 0, sizeof(struct vCard));
226         free(v);
227 }
228
229
230
231
232 /**
233  * \brief Set a name/value pair in the card
234  * \param v vCard to inspect
235  * \param name key to set
236  * \param value the value to assign to key
237  * \param append should we append the value to an existing one?
238  */
239 void vcard_set_prop(struct vCard *v, char *name, char *value, int append) {
240         int i;
241
242         if (v->magic != CTDL_VCARD_MAGIC) return;       /** Self-check */
243
244         /** If this key is already present, replace it */
245         if (!append) if (v->numprops) for (i=0; i<(v->numprops); ++i) {
246                 if (!strcasecmp(v->prop[i].name, name)) {
247                         free(v->prop[i].name);
248                         free(v->prop[i].value);
249                         v->prop[i].name = strdup(name);
250                         v->prop[i].value = strdup(value);
251                         return;
252                 }
253         }
254
255         /** Otherwise, append it */
256         ++v->numprops;
257         v->prop = realloc(v->prop,
258                 (v->numprops * sizeof(struct vCardProp)) );
259         v->prop[v->numprops-1].name = strdup(name);
260         v->prop[v->numprops-1].value = strdup(value);
261 }
262
263
264
265
266 /**
267  * \brief Serialize a struct vcard into its standard format.
268  * \param v vCard to serialize
269  * \return the serialized vCard
270  */
271 char *vcard_serialize(struct vCard *v)
272 {
273         char *ser;
274         int i, j;
275         size_t len;
276         int is_utf8 = 0;
277
278         if (v->magic != CTDL_VCARD_MAGIC) return NULL;  /** self check */
279
280         /** Figure out how big a buffer we need to allocate */
281         len = 64;       /** for begin, end, and a little padding for safety */
282         if (v->numprops) for (i=0; i<(v->numprops); ++i) {
283                 len = len +
284                         strlen(v->prop[i].name) +
285                         strlen(v->prop[i].value) + 16;
286         }
287
288         ser = malloc(len);
289         if (ser == NULL) return NULL;
290
291         safestrncpy(ser, "begin:vcard\r\n", len);
292         if (v->numprops) for (i=0; i<(v->numprops); ++i) {
293                 if ( (strcasecmp(v->prop[i].name, "end")) && (v->prop[i].value != NULL) ) {
294                         is_utf8 = 0;
295                         for (j=0; j<strlen(v->prop[i].value); ++j) {
296                                 if ( (v->prop[i].value[j] < 32) || (v->prop[i].value[j] > 126) ) {
297                                         is_utf8 = 1;
298                                 }
299                         }
300                         strcat(ser, v->prop[i].name);
301                         if (is_utf8) {
302                                 strcat(ser, ";charset=UTF-8");
303                         }
304                         strcat(ser, ":");
305                         strcat(ser, v->prop[i].value);
306                         strcat(ser, "\r\n");
307                 }
308         }
309         strcat(ser, "end:vcard\r\n");
310
311         return ser;
312 }
313
314
315
316 /*
317  * \brief       Convert FN (Friendly Name) into N (Name)
318  *
319  * \param       vname           Supplied friendly-name
320  * \param       n               Target buffer to store Name
321  * \param       vname_size      Size of buffer
322  */
323 void vcard_fn_to_n(char *vname, char *n, size_t vname_size) {
324         char lastname[256];
325         char firstname[256];
326         char middlename[256];
327         char honorific_prefixes[256];
328         char honorific_suffixes[256];
329         char buf[256];
330
331         safestrncpy(buf, n, sizeof buf);
332
333         /* Try to intelligently convert the screen name to a
334          * fully expanded vCard name based on the number of
335          * words in the name
336          */
337         safestrncpy(lastname, "", sizeof lastname);
338         safestrncpy(firstname, "", sizeof firstname);
339         safestrncpy(middlename, "", sizeof middlename);
340         safestrncpy(honorific_prefixes, "", sizeof honorific_prefixes);
341         safestrncpy(honorific_suffixes, "", sizeof honorific_suffixes);
342
343         /* Honorific suffixes */
344         if (num_tokens(buf, ',') > 1) {
345                 extract_token(honorific_suffixes, buf, (num_tokens(buf, ' ') - 1), ',',
346                         sizeof honorific_suffixes);
347                 remove_token(buf, (num_tokens(buf, ',') - 1), ',');
348         }
349
350         /* Find a last name */
351         extract_token(lastname, buf, (num_tokens(buf, ' ') - 1), ' ', sizeof lastname);
352         remove_token(buf, (num_tokens(buf, ' ') - 1), ' ');
353
354         /* Find honorific prefixes */
355         if (num_tokens(buf, ' ') > 2) {
356                 extract_token(honorific_prefixes, buf, 0, ' ', sizeof honorific_prefixes);
357                 remove_token(buf, 0, ' ');
358         }
359
360         /* Find a middle name */
361         if (num_tokens(buf, ' ') > 1) {
362                 extract_token(middlename, buf, (num_tokens(buf, ' ') - 1), ' ', sizeof middlename);
363                 remove_token(buf, (num_tokens(buf, ' ') - 1), ' ');
364         }
365
366         /* Anything left is probably the first name */
367         safestrncpy(firstname, buf, sizeof firstname);
368         striplt(firstname);
369
370         /* Compose the structured name */
371         snprintf(vname, vname_size, "%s;%s;%s;%s;%s", lastname, firstname, middlename,
372                 honorific_prefixes, honorific_suffixes);
373 }
374
375
376
377
378
379
380 /*@}*/