[evolution-data-server] Bug #656603 - Add support for generating vCard 2.1 in libebook



commit cca25e98a71d8c06f9ce1b53658ec4675f2dd5c2
Author: Bartosz Szatkowski <bulislaw linux com>
Date:   Tue Oct 18 12:29:07 2011 +0200

    Bug #656603 - Add support for generating vCard 2.1 in libebook

 addressbook/libebook/e-vcard.c    |  247 +++++++++++++++++++++++++++++++++++--
 addressbook/libebook/e-vcard.h    |    8 ++
 tests/libebook/vcard/dump-vcard.c |   17 ++-
 3 files changed, 257 insertions(+), 15 deletions(-)
---
diff --git a/addressbook/libebook/e-vcard.c b/addressbook/libebook/e-vcard.c
index 7e06c00..097dd7b 100644
--- a/addressbook/libebook/e-vcard.c
+++ b/addressbook/libebook/e-vcard.c
@@ -104,6 +104,42 @@ e_vcard_init (EVCard *evc)
 	evc->priv = g_new0 (EVCardPrivate, 1);
 }
 
+/* Case insensitive version of strstr */
+static gchar *
+strstr_nocase (const gchar *haystack,
+               const gchar *needle)
+{
+/* When _GNU_SOURCE is available, use the nonstandard extension of libc */
+#ifdef _GNU_SOURCE
+	g_return_val_if_fail (haystack, NULL);
+	g_return_Val_if_fail (needle, NULL);
+
+	return strcasestr (haystack, needle)
+#else
+/* Otherwise convert both, haystack and needle to lowercase and use good old strstr */
+	gchar *l_haystack;
+	gchar *l_needle;
+	gchar *pos;
+
+	g_return_val_if_fail (haystack, NULL);
+	g_return_val_if_fail (needle, NULL);
+
+	l_haystack = g_ascii_strdown (haystack, -1);
+	l_needle = g_ascii_strdown (needle, -1);
+	pos = strstr (l_haystack, l_needle);
+
+	/* Get actual position of the needle in the haystack instead of l_haystack or
+	 * leave it NULL */
+	if (pos)
+		pos = (gchar *)(haystack + (pos - l_haystack));
+
+	g_free (l_haystack);
+	g_free (l_needle);
+
+	return pos;
+#endif
+}
+
 /*  Skip newline characters and return the next character.
  *  This function takes care of folding lines, skipping
  *  newline characters if found, taking care of equal characters
@@ -687,6 +723,24 @@ e_vcard_ensure_attributes (EVCard *evc)
 	return evc->priv->attributes;
 }
 
+static gchar *
+e_vcard_escape_semicolons (const gchar *s)
+{
+	GString *str;
+	const gchar *p;
+
+	str = g_string_new ("");
+
+	for (p = s; p && *p; p++) {
+		if (*p == ';')
+			g_string_append (str, "\\");
+
+		g_string_append_c (str, *p);
+	}
+
+	return g_string_free (str, FALSE);
+}
+
 /**
  * e_vcard_escape_string:
  * @s: the string to escape
@@ -854,10 +908,186 @@ e_vcard_new_from_string (const gchar *str)
 }
 
 static gchar *
+e_vcard_qp_encode (const gchar *txt)
+{
+	const gchar *p = txt;
+	GString *escaped = g_string_new ("");
+	gint count = 0;
+
+	while (*p != '\0') {
+		if ((*p >= 33 && *p <= 60) || (*p >= 62 && *p <= 126)) {
+			if (count == 75) {
+				g_string_append (escaped, "=" CRLF " ");
+				count = 1; /* 1 for space needed for folding */
+			}
+
+			g_string_append_c (escaped, *p++);
+			count++;
+
+			continue;
+		}
+
+		if (count >= 73) {
+			g_string_append (escaped, "=" CRLF " ");
+			count = 1; /* 1 for space needed for folding */
+		}
+
+		g_string_append_printf (escaped, "=%2.2X", (unsigned char) *p++);
+		count += 3;
+	}
+
+	return g_string_free (escaped, FALSE);
+}
+
+static GHashTable *
+generate_dict_validator (const gchar *words)
+{
+	GHashTable *dict = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+	gchar **list = g_strsplit (words, ",", 0);
+	gint i;
+
+	for (i = 0; list[i]; i++)
+		g_hash_table_insert (dict, list[i], NULL);
+
+	g_free (list);
+
+	return dict;
+}
+
+static gchar *
 e_vcard_to_string_vcard_21 (EVCard *evc)
 {
-	g_warning ("need to implement e_vcard_to_string_vcard_21");
-	return g_strdup ("");
+	GList *l, *v;
+	GString *str = g_string_new ("");
+	GHashTable *evc_prop = generate_dict_validator (E_VCARD_21_VALID_PROPERTIES);
+	GHashTable *evc_params = generate_dict_validator (E_VCARD_21_VALID_PARAMETERS);
+
+	g_string_append (str, "BEGIN:VCARD" CRLF);
+	g_string_append (str, "VERSION:2.1" CRLF);
+
+	for (l = e_vcard_ensure_attributes (evc); l; l = l->next) {
+		GList *list;
+		EVCardAttribute *attr = l->data;
+		GString *attr_str;
+		gboolean empty, encode;
+		gchar *upper_name;
+
+		if (g_ascii_strcasecmp (attr->name, "VERSION") == 0)
+			continue;
+
+		upper_name = g_ascii_strup (attr->name, -1);
+		/* Checking whether current property (attribute) is valid for vCard 2.1 */
+		if (!g_hash_table_lookup_extended (evc_prop, upper_name, NULL, NULL)) {
+			g_free (upper_name);
+
+			continue;
+		}
+		g_free (upper_name);
+
+		empty = TRUE; /* Empty fields should be omitted -- some headsets may choke on it */
+		encode = FALSE; /* Generally only new line MUST be encoded (Quoted Printable) */
+
+		for (v = attr->values; v; v = v->next) {
+			gchar *value = v->data;
+
+			if (value && *value)
+				empty = FALSE;
+			else
+				continue;
+
+			if (strstr (value, "\n") != NULL) {
+				encode = TRUE;
+				break;
+			}
+		}
+
+		if (empty)
+			continue;
+
+		attr_str = g_string_new ("");
+
+		/* From vCard 2.1 spec page 27, 28
+		 *
+		 * contentline  = [group "."] name *(";" param) ":" value CRLF
+		 */
+
+		if (attr->group) {
+			g_string_append (attr_str, attr->group);
+			g_string_append_c (attr_str, '.');
+		}
+		g_string_append (attr_str, attr->name);
+
+		/* handle the parameters */
+		for (list = attr->params; list; list = list->next) {
+			EVCardAttributeParam *param = list->data;
+
+			/* Page 28
+			 *
+			 * param        = param-name "=" param-value
+			 */
+
+			upper_name = g_ascii_strup (param->name, -1);
+			/* Checking whether current parameter is valid for vCard 2.1 */
+			if (!g_hash_table_lookup_extended (evc_params, upper_name, NULL, NULL)) {
+				g_free (upper_name);
+
+				continue;
+			}
+			g_free (upper_name);
+
+			g_string_append_c (attr_str, ';');
+			g_string_append (attr_str, param->name);
+			if (!param->values)
+				continue;
+
+			for (v = param->values; v; v = v->next) {
+				gchar *value = v->data;
+				gchar *escaped_value = e_vcard_escape_semicolons (value);
+
+				g_string_append_printf (attr_str, "=%s", escaped_value);
+
+				if (v->next)
+					g_string_append_printf (attr_str, ";%s", param->name);
+
+				g_free (escaped_value);
+			}
+		}
+
+		if (encode)
+			g_string_append (attr_str, ";ENCODING=QUOTED-PRINTABLE");
+
+		g_string_append_c (attr_str, ':');
+
+		for (v = attr->values; v; v = v->next) {
+			gchar *value = v->data;
+
+			if (encode) {
+				gchar *escaped_value;
+
+				escaped_value = e_vcard_qp_encode (value);
+				g_string_append (attr_str, escaped_value);
+
+				g_free (escaped_value);
+			} else
+				g_string_append (attr_str, value);
+
+			if (v->next)
+				g_string_append_c (attr_str, ';');
+		}
+
+		g_string_append (attr_str, CRLF);
+
+		g_string_append (str, attr_str->str);
+
+		g_string_free (attr_str, TRUE);
+	}
+
+	g_string_append (str, "END:VCARD");
+
+	g_hash_table_destroy (evc_params);
+	g_hash_table_destroy (evc_prop);
+
+	return g_string_free (str, FALSE);
 }
 
 static gchar *
@@ -1016,17 +1246,16 @@ e_vcard_to_string (EVCard *evc,
 {
 	g_return_val_if_fail (E_IS_VCARD (evc), NULL);
 
-	/* If the vcard is not parsed yet, and if we don't have a UID in priv->attributes
-	return priv->vcard directly */
-	/* XXX: The format is ignored but it does not really matter
-	 * since only 3.0 is supported at the moment */
-	if (evc->priv->vcard != NULL && evc->priv->attributes == NULL)
-		return g_strdup (evc->priv->vcard);
-
 	switch (format) {
 	case EVC_FORMAT_VCARD_21:
+		if (evc->priv->vcard && strstr_nocase (evc->priv->vcard, CRLF "VERSION:2.1" CRLF))
+			return g_strdup (evc->priv->vcard);
+
 		return e_vcard_to_string_vcard_21 (evc);
 	case EVC_FORMAT_VCARD_30:
+		if (evc->priv->vcard && strstr_nocase (evc->priv->vcard, CRLF "VERSION:3.0" CRLF))
+			return g_strdup (evc->priv->vcard);
+
 		return e_vcard_to_string_vcard_30 (evc);
 	default:
 		g_warning ("invalid format specifier passed to e_vcard_to_string");
diff --git a/addressbook/libebook/e-vcard.h b/addressbook/libebook/e-vcard.h
index fb384bd..a4dac60 100644
--- a/addressbook/libebook/e-vcard.h
+++ b/addressbook/libebook/e-vcard.h
@@ -169,6 +169,14 @@ typedef enum {
 
 #define E_TYPE_VCARD_PARAM_ATTRIBUTE  (e_vcard_attribute_param_get_type ())
 
+#define E_VCARD_21_VALID_PROPERTIES "ADR,ORG,N,AGENT,LOGO,PHOTO,LABEL,FN,TITLE,SOUND,VERSION,TEL," \
+					"EMAIL,TZ,GEO,NOTE,URL,BDAY,ROLE,REV,UID,KEY,MAILER"
+#define E_VCARD_21_VALID_PARAMETERS "TYPE,VALUE,ENCODING,CHARSET,LANGUAGE,DOM,INTL,POSTAL,PARCEL," \
+		"HOME,WORK,PREF,VOICE,FAX,MSG,CELL,PAGER,BBS,MODEM,CAR,ISDN,VIDEO,AOL,APPLELINK," \
+		"ATTMAIL,CIS,EWORLD,INTERNET,IBMMAIL,MCIMAIL,POWERSHARE,PRODIGY,TLX,X400,GIF,CGM," \
+		"WMF,BMP,MET,PMB,DIB,PICT,TIFF,PDF,PS,JPEG,QTIME,MPEG,MPEG2,AVI,WAVE,AIFF,PCM," \
+		"X509,PGP"
+
 typedef struct _EVCard EVCard;
 typedef struct _EVCardClass EVCardClass;
 typedef struct _EVCardPrivate EVCardPrivate;
diff --git a/tests/libebook/vcard/dump-vcard.c b/tests/libebook/vcard/dump-vcard.c
index d79ce65..2a9eb4c 100644
--- a/tests/libebook/vcard/dump-vcard.c
+++ b/tests/libebook/vcard/dump-vcard.c
@@ -12,8 +12,10 @@ main (gint argc,
 	GString *str = g_string_new ("");
 	gchar *parsed_vcard;
 
-	if (argc < 2)
-	  return 0;
+	if (argc < 2) {
+		g_warning ("Requires one parameter, a vCard file\n");
+		return 1;
+	}
 
 	g_type_init_with_debug_flags (G_TYPE_DEBUG_OBJECTS);
 
@@ -27,16 +29,19 @@ main (gint argc,
 	fclose (fp);
 
 	vcard = e_vcard_new_from_string (str->str);
+	g_string_free (str, TRUE);
 
 	e_vcard_dump_structure (vcard);
 
-	parsed_vcard = e_vcard_to_string (vcard, EVC_FORMAT_VCARD_30);
+	parsed_vcard = e_vcard_to_string (vcard, EVC_FORMAT_VCARD_21);
+	printf ("\nvCard 2.1: %s\n", parsed_vcard);
+	g_free (parsed_vcard);
 
-	printf ("\nvcard: %s\n", parsed_vcard);
+	parsed_vcard = e_vcard_to_string (vcard, EVC_FORMAT_VCARD_30);
+	printf ("\nvCard 3.0: %s\n", parsed_vcard);
+	g_free (parsed_vcard);
 
 	g_object_unref (vcard);
 
-	g_free (parsed_vcard);
-
 	return 0;
 }



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]