2 * vCard implementation for Citadel
4 * Copyright (C) 1999-2023 by the citadel.org development team.
6 // This program is open source software. Use, duplication, or disclosure
7 // is subject to the terms of the GNU General Public License, version 3.
22 #include <libcitadel.h>
26 * Constructor (empty vCard)
27 * Returns an empty vcard
29 struct vCard *vcard_new() {
32 v = (struct vCard *) malloc(sizeof(struct vCard));
33 if (v == NULL) return v;
35 v->magic = CTDL_VCARD_MAGIC;
43 * Remove the "charset=" attribute from a vCard property name
46 void remove_charset_attribute(char *strbuf)
51 t = num_tokens(strbuf, ';');
53 extract_token(compare, strbuf, i, ';', sizeof compare);
55 if (!strncasecmp(compare, "charset=", 8)) {
56 remove_token(strbuf, i, ';');
59 if (!IsEmptyStr(strbuf)) {
60 if (strbuf[strlen(strbuf)-1] == ';') {
61 strbuf[strlen(strbuf)-1] = 0;
68 * Add a property to a vCard
70 * v vCard structure to which we are adding
71 * propname name of new property
72 * propvalue value of new property
74 void vcard_add_prop(struct vCard *v, const char *propname, const char *propvalue) {
76 v->prop = realloc(v->prop,
77 (v->numprops * sizeof(struct vCardProp)) );
78 v->prop[v->numprops-1].name = strdup(propname);
79 v->prop[v->numprops-1].value = strdup(propvalue);
83 * Constructor - returns a new struct vcard given a serialized vcard
85 struct vCard *VCardLoad(StrBuf *vbtext) {
86 return vcard_load((char*)ChrPtr(vbtext));
90 * Constructor - returns a new struct vcard given a serialized vcard
92 struct vCard *vcard_load(char *vtext) {
96 char *namebuf, *valuebuf;
100 if (vtext == NULL) return vcard_new();
101 mycopy = strdup(vtext);
102 if (mycopy == NULL) return NULL;
105 * First, fix this big pile o' vCard to make it more parseable.
106 * To make it easier to parse, we convert CRLF to LF, and unfold any
107 * multi-line fields into single lines.
109 for (i=0; !IsEmptyStr(&mycopy[i]); ++i) {
110 if (!strncmp(&mycopy[i], "\r\n", 2)) {
111 strcpy(&mycopy[i], &mycopy[i+1]);
113 if ( (mycopy[i]=='\n') && (isspace(mycopy[i+1])) ) {
114 strcpy(&mycopy[i], &mycopy[i+1]);
126 while (!IsEmptyStr(ptr)) {
127 colonpos = pattern2(ptr, ":");
128 nlpos = pattern2(ptr, "\n");
130 if ((nlpos > colonpos) && (colonpos > 0)) {
131 namebuf = malloc(colonpos + 1);
132 valuebuf = malloc(nlpos - colonpos + 1);
133 memcpy(namebuf, ptr, colonpos);
134 namebuf[colonpos] = '\0';
135 memcpy(valuebuf, &ptr[colonpos+1], nlpos-colonpos-1);
136 valuebuf[nlpos-colonpos-1] = '\0';
138 if (!strcasecmp(namebuf, "end")) {
141 if ( (!strcasecmp(namebuf, "begin"))
142 && (!strcasecmp(valuebuf, "vcard"))
147 if ( (valid) && (strcasecmp(namebuf, "begin")) ) {
148 remove_charset_attribute(namebuf);
150 v->prop = realloc(v->prop,
151 (v->numprops * sizeof(struct vCardProp))
153 v->prop[v->numprops-1].name = namebuf;
154 v->prop[v->numprops-1].value = valuebuf;
163 while ( (*ptr != '\n') && (!IsEmptyStr(ptr)) ) {
166 if (*ptr == '\n') ++ptr;
175 * Fetch the value of a particular key.
176 * If is_partial is set to 1, a partial match is ok (for example,
177 * a key of "tel;home" will satisfy a search for "tel").
178 * Set "instance" to a value higher than 0 to return subsequent instances
181 * Set "get_propname" to nonzero to fetch the property name instead of value.
182 * v vCard to get keyvalue from
183 * propname key to retrieve
185 * instance if nonzero return a later token of the value
186 * get_propname if nonzero get the real property name???
188 * returns the requested value / token / propertyname
190 char *vcard_get_prop(struct vCard *v, char *propname,
191 int is_partial, int instance, int get_propname) {
193 int found_instance = 0;
195 if (v->numprops) for (i=0; i<(v->numprops); ++i) {
196 if ( (!strcasecmp(v->prop[i].name, propname))
197 || (propname[0] == 0)
198 || ( (!strncasecmp(v->prop[i].name,
199 propname, strlen(propname)))
200 && (v->prop[i].name[strlen(propname)] == ';')
201 && (is_partial) ) ) {
202 if (instance == found_instance++) {
204 return(v->prop[i].name);
207 return(v->prop[i].value);
222 void vcard_free(struct vCard *v) {
225 if (v->magic != CTDL_VCARD_MAGIC) return; /* Self-check */
227 if (v->numprops) for (i=0; i<(v->numprops); ++i) {
228 free(v->prop[i].name);
229 free(v->prop[i].value);
232 if (v->prop != NULL) free(v->prop);
234 memset(v, 0, sizeof(struct vCard));
242 * Set a name/value pair in the card
243 * v vCard to manipulate
245 * value the value to assign to key
246 * append if nonzero, append rather than replace if this key already exists.
248 void vcard_set_prop(struct vCard *v, char *name, char *value, int append) {
251 if (v->magic != CTDL_VCARD_MAGIC) return; /* Self-check */
253 /* If this key is already present, replace it */
254 if (!append) if (v->numprops) for (i=0; i<(v->numprops); ++i) {
255 if (!strcasecmp(v->prop[i].name, name)) {
256 free(v->prop[i].name);
257 free(v->prop[i].value);
258 v->prop[i].name = strdup(name);
259 v->prop[i].value = strdup(value);
264 /* Otherwise, append it */
266 v->prop = realloc(v->prop,
267 (v->numprops * sizeof(struct vCardProp)) );
268 v->prop[v->numprops-1].name = strdup(name);
269 v->prop[v->numprops-1].value = strdup(value);
276 * Serialize a 'struct vcard' into an actual vcard.
278 char *vcard_serialize(struct vCard *v)
285 if (v == NULL) return NULL; /* self check */
286 if (v->magic != CTDL_VCARD_MAGIC) return NULL; /* self check */
288 /* Set the vCard version number to 2.1 at this time. */
289 vcard_set_prop(v, "VERSION", "2.1", 0);
291 /* Figure out how big a buffer we need to allocate */
292 len = 64; /* for begin, end, and a little padding for safety */
293 if (v->numprops) for (i=0; i<(v->numprops); ++i) {
295 strlen(v->prop[i].name) +
296 strlen(v->prop[i].value) + 16;
300 if (ser == NULL) return NULL;
302 safestrncpy(ser, "begin:vcard\r\n", len);
303 if (v->numprops) for (i=0; i<(v->numprops); ++i) {
304 if ( (strcasecmp(v->prop[i].name, "end")) && (v->prop[i].value != NULL) ) {
306 for (j=0; !IsEmptyStr(&v->prop[i].value[j]); ++j) {
307 if ( (v->prop[i].value[j] < 32) || (v->prop[i].value[j] > 126) ) {
311 strcat(ser, v->prop[i].name);
313 strcat(ser, ";charset=UTF-8");
316 strcat(ser, v->prop[i].value);
320 strcat(ser, "end:vcard\r\n");
328 * Convert FN (Friendly Name) into N (Name)
330 * vname Supplied friendly-name
331 * n Target buffer to store Name
332 * vname_size Size of buffer
334 void vcard_fn_to_n(char *vname, char *n, size_t vname_size) {
337 char middlename[256];
338 char honorific_prefixes[256];
339 char honorific_suffixes[256];
342 safestrncpy(buf, n, sizeof buf);
344 /* Try to intelligently convert the screen name to a
345 * fully expanded vCard name based on the number of
348 safestrncpy(lastname, "", sizeof lastname);
349 safestrncpy(firstname, "", sizeof firstname);
350 safestrncpy(middlename, "", sizeof middlename);
351 safestrncpy(honorific_prefixes, "", sizeof honorific_prefixes);
352 safestrncpy(honorific_suffixes, "", sizeof honorific_suffixes);
354 /* Honorific suffixes */
355 if (num_tokens(buf, ',') > 1) {
356 extract_token(honorific_suffixes, buf, (num_tokens(buf, ' ') - 1), ',',
357 sizeof honorific_suffixes);
358 remove_token(buf, (num_tokens(buf, ',') - 1), ',');
361 /* Find a last name */
362 extract_token(lastname, buf, (num_tokens(buf, ' ') - 1), ' ', sizeof lastname);
363 remove_token(buf, (num_tokens(buf, ' ') - 1), ' ');
365 /* Find honorific prefixes */
366 if (num_tokens(buf, ' ') > 2) {
367 extract_token(honorific_prefixes, buf, 0, ' ', sizeof honorific_prefixes);
368 remove_token(buf, 0, ' ');
371 /* Find a middle name */
372 if (num_tokens(buf, ' ') > 1) {
373 extract_token(middlename, buf, (num_tokens(buf, ' ') - 1), ' ', sizeof middlename);
374 remove_token(buf, (num_tokens(buf, ' ') - 1), ' ');
377 /* Anything left is probably the first name */
378 safestrncpy(firstname, buf, sizeof firstname);
379 string_trim(firstname);
381 /* Compose the structured name */
382 snprintf(vname, vname_size, "%s;%s;%s;%s;%s", lastname, firstname, middlename,
383 honorific_prefixes, honorific_suffixes);