[evolution-data-server] Bug #656603 - Add support for generating vCard 2.1 in libebook
- From: Milan Crha <mcrha src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution-data-server] Bug #656603 - Add support for generating vCard 2.1 in libebook
- Date: Tue, 18 Oct 2011 10:30:12 +0000 (UTC)
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]