--- /dev/null
+/*
+
+ silcvcard.c
+
+ Author: Pekka Riikonen <priikone@silcnet.org>
+
+ Copyright (C) 2002 - 2005 Pekka Riikonen
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+*/
+/* $Id$ */
+/* Implementation of the VCard (RFC 2426) */
+
+#include "silc.h"
+
+#define VCARD_HEADER "BEGIN:VCARD\n"
+#define VCARD_VERSION "VERSION:3.0\n"
+#define VCARD_FOOTER "END:VCARD\n"
+
+/* Encode VCard */
+
+unsigned char *silc_vcard_encode(SilcVCard vcard, SilcUInt32 *vcard_len)
+{
+ SilcBufferStruct buffer;
+ int i;
+
+ if (!vcard->full_name || !vcard->family_name || !vcard->first_name)
+ return NULL;
+
+ memset(&buffer, 0, sizeof(buffer));
+ silc_buffer_strformat(
+ &buffer,
+ VCARD_HEADER,
+ VCARD_VERSION,
+ "FN:", vcard->full_name, "\n",
+ "N:", vcard->family_name, ";", vcard->first_name, ";",
+ vcard->middle_names, ";", vcard->prefix, ";", vcard->suffix, "\n",
+ SILC_STRFMT_END);
+
+ if (vcard->nickname)
+ silc_buffer_strformat(&buffer,
+ "NICKNAME:", vcard->nickname, "\n",
+ SILC_STRFMT_END);
+ if (vcard->bday)
+ silc_buffer_strformat(&buffer,
+ "BDAY:", vcard->bday, "\n",
+ SILC_STRFMT_END);
+ if (vcard->title)
+ silc_buffer_strformat(&buffer,
+ "TITLE:", vcard->title, "\n",
+ SILC_STRFMT_END);
+ if (vcard->role)
+ silc_buffer_strformat(&buffer,
+ "ROLE:", vcard->role, "\n",
+ SILC_STRFMT_END);
+ if (vcard->org_name)
+ silc_buffer_strformat(&buffer,
+ "ORG:", vcard->org_name, ";", vcard->org_unit, "\n",
+ SILC_STRFMT_END);
+ if (vcard->categories)
+ silc_buffer_strformat(&buffer,
+ "CATEGORIES:", vcard->categories, "\n",
+ SILC_STRFMT_END);
+ if (vcard->catclass)
+ silc_buffer_strformat(&buffer,
+ "CLASS:", vcard->catclass, "\n",
+ SILC_STRFMT_END);
+ if (vcard->url)
+ silc_buffer_strformat(&buffer,
+ "URL:", vcard->url, "\n",
+ SILC_STRFMT_END);
+ if (vcard->label)
+ silc_buffer_strformat(&buffer,
+ "LABEL;", vcard->url, "\n",
+ SILC_STRFMT_END);
+ for (i = 0; i < vcard->num_addrs; i++) {
+ silc_buffer_strformat(&buffer,
+ "ADR;TYPE=",
+ vcard->addrs[i].type, ":",
+ vcard->addrs[i].pbox, ";",
+ vcard->addrs[i].ext_addr, ";",
+ vcard->addrs[i].street_addr, ";",
+ vcard->addrs[i].city, ";",
+ vcard->addrs[i].state, ";",
+ vcard->addrs[i].code, ";",
+ vcard->addrs[i].country, "\n",
+ SILC_STRFMT_END);
+ }
+ for (i = 0; i < vcard->num_tels; i++) {
+ silc_buffer_strformat(&buffer,
+ "TEL;TYPE=",
+ vcard->tels[i].type, ":",
+ vcard->tels[i].telnum, "\n",
+ SILC_STRFMT_END);
+ }
+ for (i = 0; i < vcard->num_emails; i++) {
+ silc_buffer_strformat(&buffer,
+ "EMAIL;TYPE=",
+ vcard->emails[i].type, ":",
+ vcard->emails[i].address, "\n",
+ SILC_STRFMT_END);
+ }
+ if (vcard->note)
+ silc_buffer_strformat(&buffer,
+ "NOTE:", vcard->note, "\n",
+ SILC_STRFMT_END);
+ if (vcard->rev)
+ silc_buffer_strformat(&buffer,
+ "REV:", vcard->rev, "\n",
+ SILC_STRFMT_END);
+
+ silc_buffer_strformat(&buffer, VCARD_FOOTER, SILC_STRFMT_END);
+
+ if (vcard_len)
+ *vcard_len = silc_buffer_truelen(&buffer);
+
+ return buffer.head;
+}
+
+/* Take one token */
+#define VCARD_TOKEN(x) \
+ if (!(x)) { \
+ (x) = silc_memdup(val + off, i - off); \
+ off = i + 1; \
+ continue; \
+ }
+
+/* Take on TYPE= token and prepare for next token, accept the
+ type also without TYPE= as it is possible */
+#define VCARD_TYPETOKEN(x) \
+ if (!(x)) { \
+ int tmpi = 0; \
+ if (!strncasecmp(val + off, "TYPE=", 5)) \
+ tmpi = 5; \
+ (x) = silc_memdup(val + off + tmpi, i - off - tmpi - 1); \
+ tmpi = off + tmpi + strlen((x)) + 1; \
+ off = i; \
+ i = tmpi; \
+ }
+
+/* Take last token */
+#define VCARD_LASTTOKEN(x) \
+ if (!(x)) { \
+ if (off < len) \
+ (x) = silc_memdup(val + off, len - off); \
+ } \
+
+/* Get one (single) field */
+#define VCARD_FIELD(val, c, x) \
+do { \
+ if (!strncasecmp(val, (c), strlen((c)))) { \
+ int tmpl = strlen((c)); \
+ if ((x)) \
+ break; \
+ if (len - tmpl > 0) \
+ (x) = silc_memdup(val + tmpl, len - tmpl); \
+ goto next; \
+ } \
+} while(0)
+
+/* Decode VCard */
+
+SilcBool silc_vcard_decode(const unsigned char *data, SilcUInt32 data_len,
+ SilcVCard vcard)
+{
+ unsigned char *val;
+ SilcBool has_begin = FALSE, has_end = FALSE;
+ int len, i, off = 0;
+
+ val = (unsigned char *)data;
+ while (val) {
+ len = 0;
+ for (i = (val - data); i < data_len; i++) {
+ if (data[i] == '\0' || data[i] == '\n') {
+ len = i - (val - data);
+ break;
+ }
+ }
+ if (!len || len > data_len - (val - data))
+ break;
+
+ /* Check for mandatory header and footer */
+ if (!strncasecmp(val, VCARD_HEADER, strlen(VCARD_HEADER))) {
+ has_begin = TRUE;
+ goto next;
+ }
+ if (!strncasecmp(val, VCARD_FOOTER, strlen(VCARD_FOOTER))) {
+ has_end = TRUE;
+ goto next;
+ }
+
+ /* Get single fields */
+ VCARD_FIELD(val, "FN:", vcard->full_name);
+ VCARD_FIELD(val, "NICKNAME:", vcard->nickname);
+ VCARD_FIELD(val, "BDAY:", vcard->bday);
+ VCARD_FIELD(val, "TITLE:", vcard->title);
+ VCARD_FIELD(val, "ROLE:", vcard->role);
+ VCARD_FIELD(val, "CATEGORIES:", vcard->categories);
+ VCARD_FIELD(val, "CLASS:", vcard->catclass);
+ VCARD_FIELD(val, "URL:", vcard->url);
+ VCARD_FIELD(val, "LABEL;", vcard->label);
+ VCARD_FIELD(val, "NOTE:", vcard->note);
+ VCARD_FIELD(val, "REV:", vcard->rev);
+
+ /* Get multi-column fields */
+
+ if (!strncasecmp(val, "N:", 2)) {
+ if (vcard->family_name)
+ break;
+ if (len - 2) {
+ off = 2;
+ for (i = off; i < len; i++)
+ if (val[i] == ';') {
+ VCARD_TOKEN(vcard->family_name);
+ VCARD_TOKEN(vcard->first_name);
+ VCARD_TOKEN(vcard->middle_names);
+ VCARD_TOKEN(vcard->prefix);
+ }
+ if (!vcard->family_name && !vcard->first_name) {
+ VCARD_LASTTOKEN(vcard->family_name);
+ off += (len - off);
+ }
+ if (!vcard->first_name) {
+ VCARD_LASTTOKEN(vcard->first_name);
+ } else {
+ VCARD_LASTTOKEN(vcard->suffix);
+ }
+ }
+ goto next;
+ }
+
+ if (!strncasecmp(val, "ORG:", 4)) {
+ if (vcard->org_name)
+ continue;
+ if (len - 4) {
+ off = 4;
+ for (i = off; i < len; i++) {
+ if (val[i] == ';') {
+ VCARD_TOKEN(vcard->org_name);
+ break;
+ }
+ }
+ /* It's possible to have ORG without last ';', so check for it */
+ if (!vcard->org_name) {
+ VCARD_LASTTOKEN(vcard->org_name);
+ } else {
+ VCARD_LASTTOKEN(vcard->org_unit);
+ }
+ }
+ goto next;
+ }
+
+ if (!strncasecmp(val, "ADR;", 4)) {
+ vcard->addrs = silc_realloc(vcard->addrs, sizeof(*vcard->addrs) *
+ (vcard->num_addrs + 1));
+ memset(&vcard->addrs[vcard->num_addrs], 0, sizeof(*vcard->addrs));
+ if (len - 4) {
+ off = 4;
+ for (i = off; i < len; i++)
+ if (val[i] == ';') {
+ VCARD_TYPETOKEN(vcard->addrs[vcard->num_addrs].type);
+ VCARD_TOKEN(vcard->addrs[vcard->num_addrs].pbox);
+ VCARD_TOKEN(vcard->addrs[vcard->num_addrs].ext_addr);
+ VCARD_TOKEN(vcard->addrs[vcard->num_addrs].street_addr);
+ VCARD_TOKEN(vcard->addrs[vcard->num_addrs].city);
+ VCARD_TOKEN(vcard->addrs[vcard->num_addrs].state);
+ VCARD_TOKEN(vcard->addrs[vcard->num_addrs].code);
+ }
+ VCARD_LASTTOKEN(vcard->addrs[vcard->num_addrs].country);
+ }
+ vcard->num_addrs++;
+ goto next;
+ }
+
+ if (!strncasecmp(val, "TEL;", 4)) {
+ vcard->tels = silc_realloc(vcard->tels, sizeof(*vcard->tels) *
+ (vcard->num_tels + 1));
+ memset(&vcard->tels[vcard->num_tels], 0, sizeof(*vcard->tels));
+ if (len - 4) {
+ off = 4;
+ for (i = off; i < len; i++)
+ if (val[i] == ':') {
+ i++;
+ VCARD_TYPETOKEN(vcard->tels[vcard->num_tels].type);
+ break;
+ }
+ VCARD_LASTTOKEN(vcard->tels[vcard->num_tels].telnum);
+ }
+ vcard->num_tels++;
+ goto next;
+ }
+
+ if (!strncasecmp(val, "EMAIL;", 6)) {
+ vcard->emails = silc_realloc(vcard->emails, sizeof(*vcard->emails) *
+ (vcard->num_emails + 1));
+ memset(&vcard->emails[vcard->num_emails], 0, sizeof(*vcard->emails));
+ if (len - 6) {
+ off = 6;
+ for (i = off; i < len; i++)
+ if (val[i] == ':') {
+ i++;
+ VCARD_TYPETOKEN(vcard->emails[vcard->num_emails].type);
+ break;
+ }
+ VCARD_LASTTOKEN(vcard->emails[vcard->num_emails].address);
+ }
+ vcard->num_emails++;
+ goto next;
+ }
+
+ next:
+ val = strchr(val, '\n');
+ if (!val)
+ break;
+ val++;
+ if (!val || !(*val))
+ break;
+ }
+
+ if (!has_begin || !has_end || !vcard->full_name) {
+ silc_vcard_free(vcard);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/* Allocate vcard context */
+
+SilcVCard silc_vcard_alloc(void)
+{
+ SilcVCard vcard = silc_calloc(1, sizeof(*vcard));
+ if (!vcard)
+ return NULL;
+ vcard->dynamic = TRUE;
+ return vcard;
+}
+
+/* Free the vcard structure */
+
+void silc_vcard_free(SilcVCard vcard)
+{
+ int i;
+
+ silc_free(vcard->full_name);
+ silc_free(vcard->family_name);
+ silc_free(vcard->first_name);
+ silc_free(vcard->middle_names);
+ silc_free(vcard->prefix);
+ silc_free(vcard->suffix);
+ silc_free(vcard->nickname);
+ silc_free(vcard->bday);
+ silc_free(vcard->title);
+ silc_free(vcard->role);
+ silc_free(vcard->org_name);
+ silc_free(vcard->org_unit);
+ silc_free(vcard->categories);
+ silc_free(vcard->catclass);
+ silc_free(vcard->url);
+ silc_free(vcard->label);
+ for (i = 0; i < vcard->num_addrs; i++) {
+ silc_free(vcard->addrs[i].type);
+ silc_free(vcard->addrs[i].pbox);
+ silc_free(vcard->addrs[i].ext_addr);
+ silc_free(vcard->addrs[i].street_addr);
+ silc_free(vcard->addrs[i].city);
+ silc_free(vcard->addrs[i].state);
+ silc_free(vcard->addrs[i].code);
+ silc_free(vcard->addrs[i].country);
+ }
+ silc_free(vcard->addrs);
+ for (i = 0; i < vcard->num_tels; i++) {
+ silc_free(vcard->tels[i].type);
+ silc_free(vcard->tels[i].telnum);
+ }
+ silc_free(vcard->tels);
+ for (i = 0; i < vcard->num_emails; i++) {
+ silc_free(vcard->emails[i].type);
+ silc_free(vcard->emails[i].address);
+ }
+ silc_free(vcard->emails);
+ silc_free(vcard->note);
+ silc_free(vcard->rev);
+ if (!vcard->dynamic)
+ memset(vcard, 0, sizeof(*vcard));
+
+ if (vcard->dynamic) {
+ memset(vcard, 0, sizeof(*vcard));
+ silc_free(vcard);
+ }
+}
+
+/* Print card to file stream */
+
+void silc_vcard_fprintf(SilcVCard vcard, FILE *stream)
+{
+ int i;
+ fprintf(stream, "%s", VCARD_HEADER);
+ fprintf(stream, "%s", VCARD_VERSION);
+ if (vcard->full_name)
+ fprintf(stream, "FN:%s\n", vcard->full_name);
+ if (vcard->family_name)
+ fprintf(stream, "N:%s;%s;%s;%s;%s\n",
+ vcard->family_name,
+ vcard->first_name ? vcard->first_name : "",
+ vcard->middle_names ? vcard->middle_names : "",
+ vcard->prefix ? vcard->prefix : "",
+ vcard->suffix ? vcard->suffix : "");
+ if (vcard->nickname)
+ fprintf(stream, "NICKNAME:%s\n", vcard->nickname);
+ if (vcard->bday)
+ fprintf(stream, "BDAY:%s\n", vcard->bday);
+ if (vcard->title)
+ fprintf(stream, "TITLE:%s\n", vcard->title);
+ if (vcard->role)
+ fprintf(stream, "ROLE:%s\n", vcard->role);
+ if (vcard->org_name)
+ fprintf(stream, "ORG:%s;%s\n", vcard->org_name,
+ vcard->org_unit ? vcard->org_unit : "");
+ if (vcard->categories)
+ fprintf(stream, "CATEGORIES:%s\n", vcard->categories);
+ if (vcard->catclass)
+ fprintf(stream, "CLASS:%s\n", vcard->catclass);
+ if (vcard->url)
+ fprintf(stream, "URL:%s\n", vcard->url);
+ if (vcard->label)
+ fprintf(stream, "LABEL;%s\n", vcard->label);
+ for (i = 0; i < vcard->num_addrs; i++) {
+ fprintf(stream, "ADR;TYPE=%s:%s;%s;%s;%s;%s;%s;%s\n",
+ vcard->addrs[i].type,
+ vcard->addrs[i].pbox ? vcard->addrs[i].pbox : "",
+ vcard->addrs[i].ext_addr ? vcard->addrs[i].ext_addr : "",
+ vcard->addrs[i].street_addr ? vcard->addrs[i].street_addr : "",
+ vcard->addrs[i].city ? vcard->addrs[i].city : "",
+ vcard->addrs[i].state ? vcard->addrs[i].state : "",
+ vcard->addrs[i].code ? vcard->addrs[i].code : "",
+ vcard->addrs[i].country ? vcard->addrs[i].country : "");
+ }
+ for (i = 0; i < vcard->num_tels; i++) {
+ fprintf(stream, "TEL;TYPE=%s:%s\n",
+ vcard->tels[i].type,
+ vcard->tels[i].telnum ? vcard->tels[i].telnum : "");
+ }
+ for (i = 0; i < vcard->num_emails; i++) {
+ fprintf(stream, "EMAIL;TYPE=%s:%s\n",
+ vcard->emails[i].type,
+ vcard->emails[i].address ? vcard->emails[i].address : "");
+ }
+ if (vcard->note)
+ fprintf(stream, "NOTE:%s\n", vcard->note);
+ if (vcard->rev)
+ fprintf(stream, "REV:%s\n", vcard->rev);
+ fprintf(stream, "%s", VCARD_FOOTER);
+ fflush(stream);
+}
--- /dev/null
+/*
+
+ silcvcard.h
+
+ Author: Pekka Riikonen <priikone@silcnet.org>
+
+ Copyright (C) 2002 Pekka Riikonen
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+*/
+
+/****h* silcvcard/SILC VCard
+ *
+ * DESCRIPTION
+ *
+ * Implementation of the VCard 3.0 standard (RFC 2426) that can be used
+ * with Requested Attributes (in WHOIS command) to deliver personal
+ * information. All fields are not supported since some of the
+ * information are not needed in context of SILC. If such VCard is
+ * received this implementation ignores the unsupported fields.
+ *
+ ***/
+
+#ifndef SILCVCARD_H
+#define SILCVCARD_H
+
+/****s* silcvcard/SilcVCard/SilcVCard
+ *
+ * NAME
+ *
+ * typedef struct { ... } SilcVCardStruct, *SilcVCard;
+ *
+ * DESCRIPTION
+ *
+ * This structure is the VCard. This holds the contents of the
+ * card. When a card is parsed it is parsed into this structure.
+ * When creating a new card application fills this structure and
+ * the library encodes the card from it. Free the allocated
+ * structure with silc_vcard_free function.
+ *
+ * SOURCE
+ */
+typedef struct {
+ char *full_name; /* full name, X.520 common name */
+ char *family_name; /* last name, string */
+ char *first_name; /* first name, string */
+ char *middle_names; /* other names, string (comma sep.) */
+ char *prefix; /* honorifix prefix (Mr., Mrs.), string */
+ char *suffix; /* honorifix suffix (MD), string (comma sep.) */
+ char *nickname; /* string (comma sep. if more than one) */
+ char *bday; /* birth day, UTC date string */
+ char *title; /* job title X.520, string */
+ char *role; /* job role X.520, string */
+ char *org_name; /* organization name, string */
+ char *org_unit; /* organization unit, string */
+ char *categories; /* application category, string */
+ char *catclass; /* class (public, private, confidental), string */
+ char *url; /* home page, URI string */
+ char *label; /* formatted address label, string (same
+ format as for 'addr' but comma sep.) */
+
+ struct addr {
+ char *type; /* address type, string
+ (intl, dom, home, work, pref, postal, parcel) */
+ char *pbox; /* post office box, string */
+ char *ext_addr; /* extended address, string */
+ char *street_addr; /* street address, string */
+ char *city; /* city, string */
+ char *state; /* state/province, string */
+ char *code; /* postal code, string */
+ char *country; /* country name, string */
+ } *addrs;
+ SilcUInt8 num_addrs; /* number of addresses */
+
+ struct tel {
+ char *type; /* telephone number type, string
+ (msg, voice, home, work, pref, bbs, modem, car,
+ cell, video, pager, isdn, fax) */
+ char *telnum; /* single telephone number, string */
+ } *tels;
+ SilcUInt8 num_tels;
+
+ struct email {
+ char *type; /* email type, string (internet, pref, x400) */
+ char *address; /* single email address, string */
+ } *emails;
+ SilcUInt8 num_emails;
+
+ char *note; /* a note, string */
+ char *rev; /* revision of card, UTC date string */
+
+ SilcBool dynamic; /* TRUE when dynamically allocated */
+} SilcVCardStruct, *SilcVCard;
+/***/
+
+/****f* silcvcard/SilcVCard/silc_vcard_encode
+ *
+ * SYNOPSIS
+ *
+ * char *silc_vcard_encode(SilcVCard vcard, SilcUInt32 *vcard_len);
+ *
+ * DESCRIPTION
+ *
+ * Encodes VCard from the SilcVCard structure indicated by `vcard'
+ * which the caller must fill before calling this function. This
+ * function encodes the card and returns allocated buffer and
+ * its length into `vcard_len'. The caller must free the returned
+ * buffer. Returns NULL on error.
+ *
+ ***/
+unsigned char *silc_vcard_encode(SilcVCard vcard, SilcUInt32 *vcard_len);
+
+/****f* silcvcard/SilcVCard/silc_vcard_decode
+ *
+ * SYNOPSIS
+ *
+ * SilcBool silc_vcard_decode(const unsigned char *data,
+ * SilcUInt32 data_len, SilcVCard vcard);
+ *
+ * DESCRIPTION
+ *
+ * Decodes VCard from the buffer `vcard' of length of `vcard_len' bytes
+ * and returns the parsed card into `vcard' structure. The caller must
+ * pre-allocate the structure. Returns TRUE if the `vcard' is valid
+ * vcard and was successfully parsed or FALSE on error. The structure
+ * is freed with silc_vcard_free function when it is not needed anymore.
+ *
+ ***/
+SilcBool silc_vcard_decode(const unsigned char *data, SilcUInt32 data_len,
+ SilcVCard vcard);
+
+/****f* silcvcard/SilcVCard/silc_vcard_alloc
+ *
+ * SYNOPSIS
+ *
+ * SilcVCard silc_vcard_alloc(void);
+ *
+ * DESCRIPTION
+ *
+ * Allocate a SilcVCard context which must be freed with the
+ * silc_vcard_free function.
+ *
+ ***/
+SilcVCard silc_vcard_alloc(void);
+
+/****f* silcvcard/SilcVCard/silc_vcard_free
+ *
+ * SYNOPSIS
+ *
+ * void silc_vcard_free(SilcVCard vcard);
+ *
+ * DESCRIPTION
+ *
+ * Free VCard structure and all data in it.
+ *
+ ***/
+void silc_vcard_free(SilcVCard vcard);
+
+/****f* silcvcard/SilcVCard/silc_vcard_fprintf
+ *
+ * SYNOPSIS
+ *
+ * void silc_vcard_fprintf(SilcVCard vcard, FILE *stream);
+ *
+ * DESCRIPTION
+ *
+ * Prints the contents of the `vcard' into file stream `stream' in
+ * the correct VCard format.
+ *
+ ***/
+void silc_vcard_fprintf(SilcVCard vcard, FILE *stream);
+
+#endif /* SILCVCARD_H */