Began making changes to do better handling of character sets.
[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 colonpos, nlpos;
88
89         if (vtext == NULL) return vcard_new();
90         mycopy = strdup(vtext);
91         if (mycopy == NULL) return NULL;
92
93         /**
94          * First, fix this big pile o' vCard to make it more parseable.
95          * To make it easier to parse, we convert CRLF to LF, and unfold any
96          * multi-line fields into single lines.
97          */
98         for (i=0; i<strlen(mycopy); ++i) {
99                 if (!strncmp(&mycopy[i], "\r\n", 2)) {
100                         strcpy(&mycopy[i], &mycopy[i+1]);
101                 }
102                 if ( (mycopy[i]=='\n') && (isspace(mycopy[i+1])) ) {
103                         strcpy(&mycopy[i], &mycopy[i+1]);
104                 }
105         }
106
107         v = vcard_new();
108         if (v == NULL) return v;
109
110         ptr = mycopy;
111         while (strlen(ptr)>0) {
112                 colonpos = (-1);
113                 nlpos = (-1);
114                 colonpos = pattern2(ptr, ":");
115                 nlpos = pattern2(ptr, "\n");
116
117                 if ((nlpos > colonpos) && (colonpos > 0)) {
118                         namebuf = malloc(colonpos + 1);
119                         valuebuf = malloc(nlpos - colonpos + 1);
120                         strncpy(namebuf, ptr, colonpos);
121                         namebuf[colonpos] = 0;
122                         strncpy(valuebuf, &ptr[colonpos+1], nlpos-colonpos-1);
123                         valuebuf[nlpos-colonpos-1] = 0;
124
125                         if (!strcasecmp(namebuf, "end")) {
126                                 valid = 0;
127                         }
128                         if (    (!strcasecmp(namebuf, "begin"))
129                                 && (!strcasecmp(valuebuf, "vcard"))
130                         ) {
131                                 valid = 1;
132                         }
133
134                         if ( (valid) && (strcasecmp(namebuf, "begin")) ) {
135                                 remove_charset_attribute(namebuf);
136                                 ++v->numprops;
137                                 v->prop = realloc(v->prop,
138                                         (v->numprops * sizeof(struct vCardProp))
139                                 );
140                                 v->prop[v->numprops-1].name = namebuf;
141                                 v->prop[v->numprops-1].value = valuebuf;
142                         } 
143                         else {
144                                 free(namebuf);
145                                 free(valuebuf);
146                         }
147
148                 }
149
150                 while ( (*ptr != '\n') && (strlen(ptr)>0) ) {
151                         ++ptr;
152                 }
153                 if (*ptr == '\n') ++ptr;
154         }
155
156         free(mycopy);
157         return v;
158 }
159
160
161 /**
162  * \brief Fetch the value of a particular key.
163  * If is_partial is set to 1, a partial match is ok (for example,
164  * a key of "tel;home" will satisfy a search for "tel").
165  * Set "instance" to a value higher than 0 to return subsequent instances
166  * of the same key.
167  * Set "get_propname" to nonzero to fetch the property name instead of value.
168  * \param v vCard to get keyvalue from
169  * \param propname key to retrieve
170  * \param is_partial dunno???
171  * \param instance if >0 return a later token of the value
172  * \param get_propname if nonzero get the real property name???
173  * \return the requested value / token / propertyname
174  */
175 char *vcard_get_prop(struct vCard *v, char *propname,
176                         int is_partial, int instance, int get_propname) {
177         int i;
178         int found_instance = 0;
179
180         if (v->numprops) for (i=0; i<(v->numprops); ++i) {
181                 if ( (!strcasecmp(v->prop[i].name, propname))
182                    || (propname[0] == 0)
183                    || (  (!strncasecmp(v->prop[i].name,
184                                         propname, strlen(propname)))
185                          && (v->prop[i].name[strlen(propname)] == ';')
186                          && (is_partial) ) ) {
187                         if (instance == found_instance++) {
188                                 if (get_propname) {
189                                         return(v->prop[i].name);
190                                 }
191                                 else {
192                                         return(v->prop[i].value);
193                                 }
194                         }
195                 }
196         }
197
198         return NULL;
199 }
200
201
202
203
204 /**
205  * \brief Destructor
206  * kill a vCard
207  * \param v the vCard to purge from memory
208  */
209 void vcard_free(struct vCard *v) {
210         int i;
211         
212         if (v->magic != CTDL_VCARD_MAGIC) return;       /* Self-check */
213         
214         if (v->numprops) for (i=0; i<(v->numprops); ++i) {
215                 free(v->prop[i].name);
216                 free(v->prop[i].value);
217         }
218
219         if (v->prop != NULL) free(v->prop);
220         
221         memset(v, 0, sizeof(struct vCard));
222         free(v);
223 }
224
225
226
227
228 /**
229  * \brief Set a name/value pair in the card
230  * \param v vCard to inspect
231  * \param name key to set
232  * \param value the value to assign to key
233  * \param append should we append the value to an existing one?
234  */
235 void vcard_set_prop(struct vCard *v, char *name, char *value, int append) {
236         int i;
237
238         if (v->magic != CTDL_VCARD_MAGIC) return;       /** Self-check */
239
240         /** If this key is already present, replace it */
241         if (!append) if (v->numprops) for (i=0; i<(v->numprops); ++i) {
242                 if (!strcasecmp(v->prop[i].name, name)) {
243                         free(v->prop[i].name);
244                         free(v->prop[i].value);
245                         v->prop[i].name = strdup(name);
246                         v->prop[i].value = strdup(value);
247                         return;
248                 }
249         }
250
251         /** Otherwise, append it */
252         ++v->numprops;
253         v->prop = realloc(v->prop,
254                 (v->numprops * sizeof(struct vCardProp)) );
255         v->prop[v->numprops-1].name = strdup(name);
256         v->prop[v->numprops-1].value = strdup(value);
257 }
258
259
260
261
262 /**
263  * \brief Serialize a struct vcard into a standard text/x-vcard MIME type.
264  * \param v vCard to serialize
265  * \return the serialized vCard
266  */
267 char *vcard_serialize(struct vCard *v)
268 {
269         char *ser;
270         int i, j;
271         size_t len;
272         int is_utf8 = 0;
273
274         if (v->magic != CTDL_VCARD_MAGIC) return NULL;  /** self check */
275
276         /** Figure out how big a buffer we need to allocate */
277         len = 64;       /** for begin, end, and a little padding for safety */
278         if (v->numprops) for (i=0; i<(v->numprops); ++i) {
279                 len = len +
280                         strlen(v->prop[i].name) +
281                         strlen(v->prop[i].value) + 16;
282         }
283
284         ser = malloc(len);
285         if (ser == NULL) return NULL;
286
287         safestrncpy(ser, "begin:vcard\r\n", len);
288         if (v->numprops) for (i=0; i<(v->numprops); ++i) {
289                 if (strcasecmp(v->prop[i].name, "end")) {
290                         is_utf8 = 0;
291                         for (j=0; i<strlen(v->prop[i].value); ++i) {
292                                 if ( (v->prop[i].value[j] < 32) || (v->prop[i].value[j] > 126) ) {
293                                         is_utf8 = 1;
294                                 }
295                         }
296                         strcat(ser, v->prop[i].name);
297                         if (is_utf8) {
298                                 strcat(ser, ";charset=UTF-8");
299                         }
300                         strcat(ser, ":");
301                         strcat(ser, v->prop[i].value);
302                         strcat(ser, "\r\n");
303                 }
304         }
305         strcat(ser, "end:vcard\r\n");
306
307         return ser;
308 }
309
310
311
312 /*
313  * \brief       Convert FN (Friendly Name) into N (Name)
314  *
315  * \param       vname           Supplied friendly-name
316  * \param       n               Target buffer to store Name
317  * \param       vname_size      Size of buffer
318  */
319 void vcard_fn_to_n(char *vname, char *n, size_t vname_size) {
320         char lastname[256];
321         char firstname[256];
322         char middlename[256];
323         char honorific_prefixes[256];
324         char honorific_suffixes[256];
325         char buf[256];
326
327         safestrncpy(buf, n, sizeof buf);
328
329         /* Try to intelligently convert the screen name to a
330          * fully expanded vCard name based on the number of
331          * words in the name
332          */
333         safestrncpy(lastname, "", sizeof lastname);
334         safestrncpy(firstname, "", sizeof firstname);
335         safestrncpy(middlename, "", sizeof middlename);
336         safestrncpy(honorific_prefixes, "", sizeof honorific_prefixes);
337         safestrncpy(honorific_suffixes, "", sizeof honorific_suffixes);
338
339         /* Honorific suffixes */
340         if (num_tokens(buf, ',') > 1) {
341                 extract_token(honorific_suffixes, buf, (num_tokens(buf, ' ') - 1), ',',
342                         sizeof honorific_suffixes);
343                 remove_token(buf, (num_tokens(buf, ',') - 1), ',');
344         }
345
346         /* Find a last name */
347         extract_token(lastname, buf, (num_tokens(buf, ' ') - 1), ' ', sizeof lastname);
348         remove_token(buf, (num_tokens(buf, ' ') - 1), ' ');
349
350         /* Find honorific prefixes */
351         if (num_tokens(buf, ' ') > 2) {
352                 extract_token(honorific_prefixes, buf, 0, ' ', sizeof honorific_prefixes);
353                 remove_token(buf, 0, ' ');
354         }
355
356         /* Find a middle name */
357         if (num_tokens(buf, ' ') > 1) {
358                 extract_token(middlename, buf, (num_tokens(buf, ' ') - 1), ' ', sizeof middlename);
359                 remove_token(buf, (num_tokens(buf, ' ') - 1), ' ');
360         }
361
362         /* Anything left is probably the first name */
363         safestrncpy(firstname, buf, sizeof firstname);
364         striplt(firstname);
365
366         /* Compose the structured name */
367         snprintf(vname, vname_size, "%s;%s;%s;%s;%s", lastname, firstname, middlename,
368                 honorific_prefixes, honorific_suffixes);
369 }
370
371
372
373
374
375
376 /*@}*/