aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChris Toshok <toshok@ximian.com>2003-03-31 13:18:28 +0800
committerChris Toshok <toshok@src.gnome.org>2003-03-31 13:18:28 +0800
commit06ea0b6cb33dff027bb23dde7d0ae19b1ff05720 (patch)
tree27dd24012b0e657f42195b01082c2c95ef7d1c9a
parent86c77578a13abb17b72f42495fe2d68b1f792318 (diff)
downloadgsoc2013-evolution-06ea0b6cb33dff027bb23dde7d0ae19b1ff05720.tar.gz
gsoc2013-evolution-06ea0b6cb33dff027bb23dde7d0ae19b1ff05720.tar.zst
gsoc2013-evolution-06ea0b6cb33dff027bb23dde7d0ae19b1ff05720.zip
braindead, and *extremely* forgiving vcard parser. not for public
2003-03-30 Chris Toshok <toshok@ximian.com> * backend/ebook/e-vcard.[ch]: braindead, and *extremely* forgiving vcard parser. not for public consumption yet. svn path=/trunk/; revision=20589
-rw-r--r--addressbook/ChangeLog5
-rw-r--r--addressbook/backend/ebook/e-vcard.c925
-rw-r--r--addressbook/backend/ebook/e-vcard.h108
3 files changed, 1038 insertions, 0 deletions
diff --git a/addressbook/ChangeLog b/addressbook/ChangeLog
index 0127291ed3..cd77ef4994 100644
--- a/addressbook/ChangeLog
+++ b/addressbook/ChangeLog
@@ -1,5 +1,10 @@
2003-03-30 Chris Toshok <toshok@ximian.com>
+ * backend/ebook/e-vcard.[ch]: braindead, and *extremely* forgiving
+ vcard parser. not for public consumption yet.
+
+2003-03-30 Chris Toshok <toshok@ximian.com>
+
[ fixes bug #39381 (again) ]
* gui/component/e-address-popup.c (edit_contact_info_cb): add
"GtkWidget *button" first arg, so we don't crash.
diff --git a/addressbook/backend/ebook/e-vcard.c b/addressbook/backend/ebook/e-vcard.c
new file mode 100644
index 0000000000..62ebda9349
--- /dev/null
+++ b/addressbook/backend/ebook/e-vcard.c
@@ -0,0 +1,925 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/* evcard.h
+ *
+ * Copyright (C) 2003 Ximian, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Chris Toshok (toshok@ximian.com)
+ */
+
+#include <glib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include "e-vcard.h"
+
+#define CRLF "\r\n"
+
+struct _EVCardPrivate {
+ GList *attributes;
+};
+
+struct _EVCardAttribute {
+ char *group;
+ char *name;
+ GList *params; /* EVCardParam */
+ GList *values;
+};
+
+struct _EVCardAttributeParam {
+ char *name;
+ GList *values; /* GList of char*'s*/
+};
+
+static GObjectClass *parent_class;
+
+static void
+e_vcard_dispose (GObject *object)
+{
+ EVCard *evc = E_VCARD (object);
+
+ if (!evc->priv)
+ return;
+
+ g_list_foreach (evc->priv->attributes, (GFunc)e_vcard_attribute_free, NULL);
+ g_list_free (evc->priv->attributes);
+
+ g_free (evc->priv);
+ evc->priv = NULL;
+
+ if (G_OBJECT_CLASS (parent_class)->dispose)
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+e_vcard_class_init (EVCardClass *klass)
+{
+ GObjectClass *object_class;
+
+ object_class = G_OBJECT_CLASS(klass);
+
+ parent_class = g_type_class_ref (G_TYPE_OBJECT);
+
+ object_class->dispose = e_vcard_dispose;
+}
+
+static void
+e_vcard_init (EVCard *evc)
+{
+ evc->priv = g_new0 (EVCardPrivate, 1);
+}
+
+GType
+e_vcard_get_type (void)
+{
+ static GType vcard_type = 0;
+
+ if (!vcard_type) {
+ static const GTypeInfo vcard_info = {
+ sizeof (EVCardClass),
+ NULL, /* base_init */
+ NULL, /* base_finalize */
+ (GClassInitFunc) e_vcard_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (EVCard),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) e_vcard_init,
+ };
+
+ vcard_type = g_type_register_static (G_TYPE_OBJECT, "EVCard", &vcard_info, 0);
+ }
+
+ return vcard_type;
+}
+
+
+
+static char*
+fold_lines (char *buf)
+{
+ GString *str = g_string_new ("");
+ char *p = buf;
+ char *next, *next2;
+
+ /* we're pretty liberal with line folding here. We handle
+ lines folded with \r\n<WS>... and \n\r<WS>... and
+ \n<WS>... We also turn single \r's and \n's not followed by
+ WS into \r\n's. */
+ while (*p) {
+ if (*p == '\r' || *p == '\n') {
+ next = g_utf8_next_char (p);
+ if (*next == '\n' || *next == '\r') {
+ next2 = g_utf8_next_char (next);
+ if (*next2 == ' ' || *next2 == '\t') {
+ p = g_utf8_next_char (next2);
+ }
+ else {
+ str = g_string_append (str, CRLF);
+ p = g_utf8_next_char (next);
+ }
+ }
+ else if (*next == ' ' || *next == '\t') {
+ p = g_utf8_next_char (next);
+ }
+ else {
+ str = g_string_append (str, CRLF);
+ p = g_utf8_next_char (p);
+ }
+ }
+ else {
+ str = g_string_append_unichar (str, g_utf8_get_char (p));
+ p = g_utf8_next_char (p);
+ }
+ }
+
+ g_free (buf);
+
+ return g_string_free (str, FALSE);
+}
+
+/* skip forward until we hit the CRLF, or \0 */
+static void
+skip_to_next_line (char **p)
+{
+ char *lp;
+ lp = *p;
+
+ while (*lp != '\r' && *lp != '\0')
+ lp = g_utf8_next_char (lp);
+
+ if (*lp == '\r') {
+ lp = g_utf8_next_char (lp); /* \n */
+ lp = g_utf8_next_char (lp); /* start of the next line */
+ }
+
+ *p = lp;
+}
+
+/* skip forward until we hit a character in @s, CRLF, or \0 */
+static void
+skip_until (char **p, char *s)
+{
+ /* XXX write me plz k thx */
+ g_assert_not_reached();
+}
+
+static void
+read_attribute_value (EVCardAttribute *attr, char **p, gboolean quoted_printable)
+{
+ char *lp = *p;
+ GString *str;
+
+ /* read in the value */
+ str = g_string_new ("");
+ while (*lp != '\r' && *lp != '\0') {
+ if (*lp == '=' && quoted_printable) {
+ char a, b;
+ if ((a = *(++lp)) == '\0') break;
+ if ((b = *(++lp)) == '\0') break;
+ if (a == '\r' && b == '\n') {
+ /* it was a = at the end of the line,
+ * just ignore this and continue
+ * parsing on the next line. yay for
+ * 2 kinds of line folding
+ */
+ }
+ else if (isalnum(a) && isalnum (b)) {
+ char c;
+
+ a = tolower (a);
+ b = tolower (b);
+
+ c = (((a>='a'?a-'a'+10:a-'0')&0x0f) << 4)
+ | ((b>='a'?b-'a'+10:b-'0')&0x0f);
+
+ str = g_string_append_c (str, c);
+ }
+ /* silently consume malformed input, and
+ continue parsing */
+ lp++;
+ }
+ else if (*lp == '\\') {
+ /* convert back to the non-escaped version of
+ the characters */
+ lp = g_utf8_next_char(lp);
+ if (*lp == '\0') {
+ str = g_string_append_c (str, '\\');
+ break;
+ }
+ switch (*lp) {
+ case 'n': str = g_string_append_c (str, '\n'); break;
+ case 'r': str = g_string_append_c (str, '\r'); break;
+ case ';': str = g_string_append_c (str, ';'); break;
+ case ',': str = g_string_append_c (str, ','); break;
+ case '\\': str = g_string_append_c (str, '\\'); break;
+ default:
+ g_warning ("invalid escape, passing it through");
+ str = g_string_append_c (str, '\\');
+ str = g_string_append_unichar (str, g_utf8_get_char(lp));
+ lp = g_utf8_next_char(lp);
+ break;
+ }
+ }
+ else if (*lp == ';') {
+ e_vcard_attribute_add_value (attr, g_string_free (str, FALSE));
+ str = g_string_new ("");
+ lp = g_utf8_next_char(lp);
+ }
+ else {
+ str = g_string_append_unichar (str, g_utf8_get_char (lp));
+ lp = g_utf8_next_char(lp);
+ }
+ }
+ if (str)
+ e_vcard_attribute_add_value (attr, g_string_free (str, FALSE));
+
+ if (*lp == '\r') {
+ lp = g_utf8_next_char (lp); /* \n */
+ lp = g_utf8_next_char (lp); /* start of the next line */
+ }
+
+ *p = lp;
+}
+
+static void
+read_attribute_params (EVCardAttribute *attr, char **p, gboolean *quoted_printable)
+{
+ char *lp = *p;
+ GString *str;
+ EVCardAttributeParam *param = NULL;
+
+ str = g_string_new ("");
+ while (*lp != '\0') {
+ /* accumulate until we hit the '=' or ';'. If we hit
+ * a '=' the string contains the parameter name. if
+ * we hit a ';' the string contains the parameter
+ * value and the name is either ENCODING (if value ==
+ * QUOTED-PRINTABLE) or TYPE (in any other case.)
+ */
+ if (*lp == '=') {
+ if (str->len > 0) {
+ param = e_vcard_attribute_param_new (str->str);
+ str = g_string_assign (str, "");
+ lp = g_utf8_next_char (lp);
+ }
+ else {
+ skip_until (&lp, ":;");
+ if (*lp == '\r') {
+ lp = g_utf8_next_char (lp); /* \n */
+ lp = g_utf8_next_char (lp); /* start of the next line */
+ break;
+ }
+ else if (*lp == ';')
+ lp = g_utf8_next_char (lp);
+ }
+ }
+ else if (*lp == ';' || *lp == ':' || *lp == ',') {
+ gboolean colon = (*lp == ':');
+ gboolean comma = (*lp == ',');
+
+ if (param) {
+ if (str->len > 0) {
+ e_vcard_attribute_param_add_value (param, str->str);
+ str = g_string_assign (str, "");
+ if (!colon)
+ lp = g_utf8_next_char (lp);
+ }
+ else {
+ /* we've got a parameter of the form:
+ * PARAM=(.*,)?[:;]
+ * so what we do depends on if there are already values
+ * for the parameter. If there are, we just finish
+ * this parameter and skip past the offending character
+ * (unless it's the ':'). If there aren't values, we free
+ * the parameter then skip past the character.
+ */
+ if (!param->values) {
+ e_vcard_attribute_param_free (param);
+ param = NULL;
+ }
+ }
+
+ if (param
+ && !g_ascii_strcasecmp (param->name, "encoding")
+ && !g_ascii_strcasecmp (param->values->data, "quoted-printable")) {
+ *quoted_printable = TRUE;
+ e_vcard_attribute_param_free (param);
+ param = NULL;
+ }
+ }
+ else {
+ if (str->len > 0) {
+ char *param_name;
+ if (!g_ascii_strcasecmp (str->str,
+ "quoted-printable")) {
+ param_name = NULL;
+ *quoted_printable = TRUE;
+ }
+ else {
+ param_name = "TYPE";
+ }
+
+ if (param_name) {
+ param = e_vcard_attribute_param_new (param_name);
+ e_vcard_attribute_param_add_value (param, str->str);
+ }
+ str = g_string_assign (str, "");
+ if (!colon)
+ lp = g_utf8_next_char (lp);
+ }
+ else {
+ /* XXX more here */
+ g_assert_not_reached ();
+ }
+ }
+ if (param && !comma) {
+ e_vcard_attribute_add_param (attr, param);
+ param = NULL;
+ }
+ if (colon)
+ break;
+ }
+ else if (g_unichar_isalnum (g_utf8_get_char (lp)) || *lp == '-' || *lp == '_') {
+ str = g_string_append_unichar (str, g_utf8_get_char (lp));
+ lp = g_utf8_next_char (lp);
+ }
+ else {
+ g_warning ("invalid character found in parameter spec");
+ str = g_string_assign (str, "");
+ skip_until (&lp, ":;");
+ }
+ }
+
+ if (str)
+ g_string_free (str, TRUE);
+
+ *p = lp;
+}
+
+/* reads an entire attribute from the input buffer, leaving p pointing
+ at the start of the next line (past the \r\n) */
+static EVCardAttribute*
+read_attribute (char **p)
+{
+ char *attr_group = NULL;
+ char *attr_name = NULL;
+ EVCardAttribute *attr = NULL;
+ GString *str;
+ char *lp = *p;
+ gboolean is_qp = FALSE;
+
+ /* first read in the group/name */
+ str = g_string_new ("");
+ while (*lp != '\r' && *lp != '\0') {
+ if (*lp == ':' || *lp == ';') {
+ if (str->len != 0) {
+ /* we've got a name, break out to the value/attribute parsing */
+ attr_name = g_string_free (str, FALSE);
+ break;
+ }
+ else {
+ /* a line of the form:
+ * (group.)?[:;]
+ *
+ * since we don't have an attribute
+ * name, skip to the end of the line
+ * and try again.
+ */
+ g_string_free (str, TRUE);
+ *p = lp;
+ skip_to_next_line(p);
+ goto lose;
+ }
+ }
+ else if (*lp == '.') {
+ if (attr_group) {
+ g_warning ("extra `.' in attribute specification. ignoring extra group `%s'",
+ str->str);
+ g_string_free (str, TRUE);
+ str = g_string_new ("");
+ }
+ if (str->len != 0) {
+ attr_group = g_string_free (str, FALSE);
+ str = g_string_new ("");
+ }
+ }
+ else if (g_unichar_isalnum (g_utf8_get_char (lp)) || *lp == '-' || *lp == '_') {
+ str = g_string_append_unichar (str, g_utf8_get_char (lp));
+ }
+ else {
+ g_warning ("invalid character found in attribute group/name");
+ g_string_free (str, TRUE);
+ *p = lp;
+ skip_to_next_line(p);
+ goto lose;
+ }
+
+ lp = g_utf8_next_char(lp);
+ }
+
+ if (!attr_name) {
+ skip_to_next_line (p);
+ goto lose;
+ }
+
+ attr = e_vcard_attribute_new (attr_group, attr_name);
+ g_free (attr_group);
+ g_free (attr_name);
+
+ if (*lp == ';') {
+ /* skip past the ';' */
+ lp = g_utf8_next_char(lp);
+ read_attribute_params (attr, &lp, &is_qp);
+ }
+ if (*lp == ':') {
+ /* skip past the ':' */
+ lp = g_utf8_next_char(lp);
+ read_attribute_value (attr, &lp, is_qp);
+ }
+
+ *p = lp;
+
+ if (!attr->values)
+ goto lose;
+
+ return attr;
+ lose:
+ if (attr)
+ e_vcard_attribute_free (attr);
+ return NULL;
+}
+
+/* we try to be as forgiving as we possibly can here - this isn't a
+ * validator. Almost nothing is considered a fatal error. We always
+ * try to return *something*.
+ */
+static void
+parse (EVCard *evc, const char *str)
+{
+ char *buf = g_strdup (str);
+ char *p, *end;
+ EVCardAttribute *attr;
+
+ /* first validate the string is valid utf8 */
+ if (!g_utf8_validate (buf, -1, (const char **)&end)) {
+ /* if the string isn't valid, we parse as much as we can from it */
+ g_warning ("invalid utf8 passed to EVCard. Limping along.");
+ *end = '\0';
+ }
+
+ printf ("BEFORE FOLDING:\n");
+ printf (str);
+
+ buf = fold_lines (buf);
+
+ printf ("\n\nAFTER FOLDING:\n");
+ printf (buf);
+
+ p = buf;
+
+ attr = read_attribute (&p);
+ if (!attr || attr->group || g_ascii_strcasecmp (attr->name, "begin")) {
+ g_warning ("vcard began without a BEGIN:VCARD\n");
+ }
+
+ while (*p) {
+ EVCardAttribute *next_attr = read_attribute (&p);
+
+ if (next_attr) {
+ if (g_ascii_strcasecmp (next_attr->name, "end"))
+ e_vcard_add_attribute (evc, next_attr);
+ attr = next_attr;
+ }
+ }
+
+ if (!attr || attr->group || g_ascii_strcasecmp (attr->name, "end")) {
+ g_warning ("vcard ended without END:VCARD\n");
+ }
+}
+
+static char*
+escape_string (const char *s)
+{
+ GString *str = g_string_new ("");
+ const char *p;
+
+ /* Escape a string as described in RFC2426, section 5 */
+ for (p = s; *p; p++) {
+ switch (*p) {
+ case '\n':
+ str = g_string_append (str, "\\n");
+ break;
+ case '\r':
+ if (*(p+1) == '\n')
+ p++;
+ str = g_string_append (str, "\\n");
+ break;
+ case ';':
+ str = g_string_append (str, "\\;");
+ break;
+ case ',':
+ str = g_string_append (str, "\\,");
+ break;
+ case '\\':
+ str = g_string_append (str, "\\\\");
+ break;
+ default:
+ str = g_string_append_c (str, *p);
+ break;
+ }
+ }
+
+ return g_string_free (str, FALSE);
+}
+
+#if notyet
+static char*
+unescape_string (const char *s)
+{
+ GString *str = g_string_new ("");
+ const char *p;
+
+ /* Unescape a string as described in RFC2426, section 5 */
+ for (p = s; *p; p++) {
+ if (*p == '\\') {
+ p++;
+ if (*p == '\0') {
+ str = g_string_append_c (str, '\\');
+ break;
+ }
+ switch (*p) {
+ case 'n': str = g_string_append_c (str, '\n'); break;
+ case 'r': str = g_string_append_c (str, '\r'); break;
+ case ';': str = g_string_append_c (str, ';'); break;
+ case ',': str = g_string_append_c (str, ','); break;
+ case '\\': str = g_string_append_c (str, '\\'); break;
+ default:
+ g_warning ("invalid escape, passing it through");
+ str = g_string_append_c (str, '\\');
+ str = g_string_append_unichar (str, g_utf8_get_char(p));
+ break;
+ }
+ }
+ }
+
+ return g_string_free (str, FALSE);
+}
+#endif
+
+EVCard *
+e_vcard_new ()
+{
+ return g_object_new (E_TYPE_VCARD, NULL);
+}
+
+EVCard *
+e_vcard_new_from_string (const char *str)
+{
+ EVCard *evc = e_vcard_new ();
+
+ parse (evc, str);
+
+ return evc;
+}
+
+char*
+e_vcard_to_string (EVCard *evc)
+{
+ GList *l;
+ GList *v;
+
+ GString *str = g_string_new ("");
+
+ str = g_string_append (str, "BEGIN:vCard" CRLF);
+
+ for (l = evc->priv->attributes; l; l = l->next) {
+ GList *p;
+ EVCardAttribute *attr = l->data;
+ GString *attr_str = g_string_new ("");
+ int l;
+
+ /* From rfc2425, 5.8.2
+ *
+ * contentline = [group "."] name *(";" param) ":" value CRLF
+ */
+
+ if (attr->group) {
+ attr_str = g_string_append (attr_str, attr->group);
+ attr_str = g_string_append_c (attr_str, '.');
+ }
+ attr_str = g_string_append (attr_str, attr->name);
+
+ /* handle the parameters */
+ for (p = attr->params; p; p = p->next) {
+ EVCardAttributeParam *param = p->data;
+ /* 5.8.2:
+ * param = param-name "=" param-value *("," param-value)
+ */
+ attr_str = g_string_append_c (attr_str, ';');
+ attr_str = g_string_append (attr_str, param->name);
+ if (param->values) {
+ attr_str = g_string_append_c (attr_str, '=');
+ for (v = param->values; v; v = v->next) {
+ char *value = v->data;
+ attr_str = g_string_append (attr_str, value);
+ if (v->next)
+ attr_str = g_string_append_c (attr_str, ',');
+ }
+ }
+ }
+
+ attr_str = g_string_append_c (attr_str, ':');
+
+ for (v = attr->values; v; v = v->next) {
+ char *value = v->data;
+ char *escaped_value = NULL;
+
+ escaped_value = escape_string (value);
+
+ attr_str = g_string_append (attr_str, escaped_value);
+ if (v->next)
+ attr_str = g_string_append_c (attr_str, ';');
+
+ g_free (escaped_value);
+ }
+
+ /* 5.8.2:
+ * When generating a content line, lines longer than 75
+ * characters SHOULD be folded
+ */
+ l = 0;
+ do {
+ if (attr_str->len - l > 75) {
+ l += 75;
+ attr_str = g_string_insert_len (attr_str, l, CRLF " ", sizeof (CRLF " ") - 1);
+ }
+ else
+ break;
+ } while (l < attr_str->len);
+
+ attr_str = g_string_append (attr_str, CRLF);
+
+ str = g_string_append (str, g_string_free (attr_str, FALSE));
+ }
+
+ str = g_string_append (str, "END:vCard");
+
+ return g_string_free (str, FALSE);
+}
+
+void
+e_vcard_dump_structure (EVCard *evc)
+{
+ GList *a;
+ GList *v;
+ int i;
+
+ printf ("vCard\n");
+ for (a = evc->priv->attributes; a; a = a->next) {
+ GList *p;
+ EVCardAttribute *attr = a->data;
+ printf ("+-- %s\n", attr->name);
+ if (attr->params) {
+ printf (" +- params=\n");
+
+ for (p = attr->params, i = 0; p; p = p->next, i++) {
+ EVCardAttributeParam *param = p->data;
+ printf (" | [%d] = %s", i,param->name);
+ printf ("(");
+ for (v = param->values; v; v = v->next) {
+ char *value = escape_string ((char*)v->data);
+ printf ("%s", value);
+ if (v->next)
+ printf (",");
+ g_free (value);
+ }
+
+ printf (")\n");
+ }
+ }
+ printf (" +- values=\n");
+ for (v = attr->values, i = 0; v; v = v->next, i++) {
+ printf (" [%d] = `%s'\n", i, (char*)v->data);
+ }
+ }
+}
+
+
+EVCardAttribute*
+e_vcard_attribute_new (const char *attr_group, const char *attr_name)
+{
+ EVCardAttribute *attr = g_new0 (EVCardAttribute, 1);
+
+ attr->group = g_strdup (attr_group);
+ attr->name = g_strdup (attr_name);
+
+ return attr;
+}
+
+void
+e_vcard_attribute_free (EVCardAttribute *attr)
+{
+ GList *p;
+
+ g_free (attr->group);
+ g_free (attr->name);
+
+ g_list_foreach (attr->values, (GFunc)g_free, NULL);
+ g_list_free (attr->values);
+
+ for (p = attr->params; p; p = p->next) {
+ EVCardAttributeParam *param = p->data;
+
+ g_free (param->name);
+ g_list_foreach (param->values, (GFunc)g_free, NULL);
+ g_list_free (param->values);
+ g_free (param);
+ }
+
+ g_free (attr);
+}
+
+void
+e_vcard_add_attribute (EVCard *evc, EVCardAttribute *attr)
+{
+ evc->priv->attributes = g_list_append (evc->priv->attributes, attr);
+}
+
+void
+e_vcard_add_attribute_with_value (EVCard *evcard,
+ EVCardAttribute *attr, const char *value)
+{
+ e_vcard_attribute_add_value (attr, value);
+
+ e_vcard_add_attribute (evcard, attr);
+}
+
+void
+e_vcard_add_attribute_with_values (EVCard *evcard, EVCardAttribute *attr, ...)
+{
+ va_list ap;
+ char *v;
+
+ va_start (ap, attr);
+
+ while ((v = va_arg (ap, char*))) {
+ e_vcard_attribute_add_value (attr, v);
+ }
+
+ va_end (ap);
+
+ e_vcard_add_attribute (evcard, attr);
+}
+
+void
+e_vcard_attribute_add_value (EVCardAttribute *attr, const char *value)
+{
+ attr->values = g_list_append (attr->values, g_strdup (value));
+}
+
+void
+e_vcard_attribute_add_values (EVCardAttribute *attr,
+ ...)
+{
+ va_list ap;
+ char *v;
+
+ va_start (ap, attr);
+
+ while ((v = va_arg (ap, char*))) {
+ e_vcard_attribute_add_value (attr, v);
+ }
+
+ va_end (ap);
+}
+
+
+EVCardAttributeParam*
+e_vcard_attribute_param_new (const char *name)
+{
+ EVCardAttributeParam *param = g_new0 (EVCardAttributeParam, 1);
+ param->name = g_strdup (name);
+
+ return param;
+}
+
+void
+e_vcard_attribute_param_free (EVCardAttributeParam *param)
+{
+ g_free (param->name);
+ g_list_foreach (param->values, (GFunc)g_free, NULL);
+ g_list_free (param->values);
+ g_free (param);
+}
+
+void
+e_vcard_attribute_add_param (EVCardAttribute *attr,
+ EVCardAttributeParam *param)
+{
+ attr->params = g_list_append (attr->params, param);
+}
+
+void
+e_vcard_attribute_param_add_value (EVCardAttributeParam *param,
+ const char *value)
+{
+ param->values = g_list_append (param->values, g_strdup (value));
+}
+
+void
+e_vcard_attribute_param_add_values (EVCardAttributeParam *param,
+ ...)
+{
+ va_list ap;
+ char *v;
+
+ va_start (ap, param);
+
+ while ((v = va_arg (ap, char*))) {
+ e_vcard_attribute_param_add_value (param, v);
+ }
+
+ va_end (ap);
+}
+
+void
+e_vcard_attribute_add_param_with_value (EVCardAttribute *attr,
+ EVCardAttributeParam *param, const char *value)
+{
+ e_vcard_attribute_param_add_value (param, value);
+
+ e_vcard_attribute_add_param (attr, param);
+}
+
+void
+e_vcard_attribute_add_param_with_values (EVCardAttribute *attr,
+ EVCardAttributeParam *param, ...)
+{
+ va_list ap;
+ char *v;
+
+ va_start (ap, param);
+
+ while ((v = va_arg (ap, char*))) {
+ e_vcard_attribute_param_add_value (param, v);
+ }
+
+ va_end (ap);
+
+ e_vcard_attribute_add_param (attr, param);
+}
+
+GList*
+e_vcard_get_attributes (EVCard *evcard)
+{
+ return evcard->priv->attributes;
+}
+
+const char*
+e_vcard_attribute_get_group (EVCardAttribute *attr)
+{
+ return attr->group;
+}
+
+const char*
+e_vcard_attribute_get_name (EVCardAttribute *attr)
+{
+ return attr->name;
+}
+
+GList*
+e_vcard_attribute_get_values (EVCardAttribute *attr)
+{
+ return attr->values;
+}
+
+GList*
+e_vcard_attribute_get_params (EVCardAttribute *attr)
+{
+ return attr->params;
+}
+
+const char*
+e_vcard_attribute_param_get_name (EVCardAttributeParam *param)
+{
+ return param->name;
+}
+
+GList*
+e_vcard_attribute_param_get_values (EVCardAttributeParam *param)
+{
+ return param->values;
+}
diff --git a/addressbook/backend/ebook/e-vcard.h b/addressbook/backend/ebook/e-vcard.h
new file mode 100644
index 0000000000..69e0a3e10b
--- /dev/null
+++ b/addressbook/backend/ebook/e-vcard.h
@@ -0,0 +1,108 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/* e-vcard.h
+ *
+ * Copyright (C) 2003 Ximian, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Chris Toshok (toshok@ximian.com)
+ */
+
+#ifndef _EVCARD_H
+#define _EVCARD_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#define EVC_FN "FN"
+#define EVC_ORG "ORG"
+#define EVC_URL "URL"
+#define EVC_VERSION "VERSION"
+#define EVC_REV "REV"
+#define EVC_PRODID "PRODID"
+#define EVC_TYPE "TYPE"
+#define EVC_ADR "ADR"
+#define EVC_TEL "TEL"
+
+#define EVC_ENCODING "ENCODING"
+#define EVC_QUOTEDPRINTABLE "QUOTED-PRINTABLE"
+
+#define E_TYPE_VCARD (e_vcard_get_type ())
+#define E_VCARD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), E_TYPE_VCARD, EVCard))
+#define E_VCARD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), E_TYPE_VCARD, EVCardClass))
+#define E_IS_VCARD(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), E_TYPE_VCARD))
+#define E_IS_VCARD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), E_TYPE_VCARD))
+#define E_VCARD_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), E_TYPE_VCARD, EVCardClass))
+
+typedef struct _EVCard EVCard;
+typedef struct _EVCardClass EVCardClass;
+typedef struct _EVCardPrivate EVCardPrivate;
+typedef struct _EVCardAttribute EVCardAttribute;
+typedef struct _EVCardAttributeParam EVCardAttributeParam;
+
+struct _EVCard {
+ GObject parent;
+
+ EVCardPrivate *priv;
+};
+
+struct _EVCardClass {
+ GObjectClass parent_class;
+};
+
+GType e_vcard_get_type (void);
+EVCard* e_vcard_new (void);
+EVCard* e_vcard_new_from_string (const char *str);
+char* e_vcard_to_string (EVCard *evcard);
+/* mostly for debugging */
+void e_vcard_dump_structure (EVCard *evc);
+
+
+/* attributes */
+EVCardAttribute *e_vcard_attribute_new (const char *attr_group, const char *attr_name);
+void e_vcard_attribute_free (EVCardAttribute *attr);
+void e_vcard_add_attribute (EVCard *evcard, EVCardAttribute *attr);
+void e_vcard_add_attribute_with_value (EVCard *evcard, EVCardAttribute *attr, const char *value);
+void e_vcard_add_attribute_with_values (EVCard *evcard, EVCardAttribute *attr, ...);
+void e_vcard_attribute_add_value (EVCardAttribute *attr, const char *value);
+void e_vcard_attribute_add_values (EVCardAttribute *attr, ...);
+
+/* attribute parameters */
+EVCardAttributeParam* e_vcard_attribute_param_new (const char *param_name);
+void e_vcard_attribute_param_free (EVCardAttributeParam *param);
+void e_vcard_attribute_add_param (EVCardAttribute *attr, EVCardAttributeParam *param);
+void e_vcard_attribute_add_param_with_value (EVCardAttribute *attr,
+ EVCardAttributeParam *param, const char *value);
+void e_vcard_attribute_add_param_with_values (EVCardAttribute *attr,
+ EVCardAttributeParam *param, ...);
+
+void e_vcard_attribute_param_add_value (EVCardAttributeParam *param,
+ const char *value);
+void e_vcard_attribute_param_add_values (EVCardAttributeParam *param,
+ ...);
+
+/* EVCard* accessors. nothing returned from these functions should be
+ freed by the caller. */
+GList* e_vcard_get_attributes (EVCard *evcard);
+const char* e_vcard_attribute_get_group (EVCardAttribute *attr);
+const char* e_vcard_attribute_get_name (EVCardAttribute *attr);
+GList* e_vcard_attribute_get_values (EVCardAttribute *attr);
+
+GList* e_vcard_attribute_get_params (EVCardAttribute *attr);
+const char* e_vcard_attribute_param_get_name (EVCardAttributeParam *param);
+GList* e_vcard_attribute_param_get_values (EVCardAttributeParam *param);
+
+
+#endif /* _EVCARD_H */