summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMilan Crha <mcrha@redhat.com>2013-10-10 19:08:20 +0200
committerMilan Crha <mcrha@redhat.com>2013-10-10 19:09:19 +0200
commita79ca0954686837335576da51f13fc4215fc0d47 (patch)
tree037c8cf9a61511f0926a53952e486a49bfd8ed7e
parent496e2b2cc13c09d98095c39bb42bdc1bc917c3fe (diff)
downloadevolution-data-server-a79ca0954686837335576da51f13fc4215fc0d47.tar.gz
Bug #695232 - Finish EVCard quoted-printable handling
-rw-r--r--addressbook/libebook-contacts/e-vcard.c137
-rw-r--r--tests/libebook-contacts/test-vcard-parsing.c159
2 files changed, 277 insertions, 19 deletions
diff --git a/addressbook/libebook-contacts/e-vcard.c b/addressbook/libebook-contacts/e-vcard.c
index 2654690cc..f6d521892 100644
--- a/addressbook/libebook-contacts/e-vcard.c
+++ b/addressbook/libebook-contacts/e-vcard.c
@@ -254,27 +254,30 @@ read_attribute_value (EVCardAttribute *attr,
if (*lp == '=' && quoted_printable) {
gchar a, b;
- if ((a = *(++lp)) == '\0') break;
- if ((b = *(++lp)) == '\0') break;
+
+ /* it's for the '=' */
+ lp++;
+ lp = skip_newline (lp, quoted_printable);
+
+ if ((a = *(lp++)) == '\0') break;
+ lp = skip_newline (lp, quoted_printable);
+
+ if ((b = *(lp++)) == '\0') break;
if (isxdigit (a) && isxdigit (b)) {
gchar 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);
+ c = (((a >= 'a' ? a - 'a' + 10 : a - '0') & 0x0f) << 4)
+ | ((b >= 'a' ? b - 'a' + 10 : b - '0') & 0x0f);
g_string_append_c (str, c); /* add decoded byte (this is not a unicode yet) */
+ } else {
+ g_string_append_c (str, '=');
+ g_string_append_c (str, a);
+ g_string_append_c (str, b);
}
- else
- {
- g_string_append_c (str, a);
- g_string_append_c (str, b);
- }
-
- lp++;
-
} else if (*lp == '\\') {
/* convert back to the non-escaped version of
* the characters */
@@ -919,7 +922,8 @@ e_vcard_new_from_string (const gchar *str)
}
static gchar *
-e_vcard_qp_encode (const gchar *txt)
+e_vcard_qp_encode (const gchar *txt,
+ gboolean can_wrap)
{
const gchar *p = txt;
GString *escaped = g_string_new ("");
@@ -927,7 +931,7 @@ e_vcard_qp_encode (const gchar *txt)
while (*p != '\0') {
if ((*p >= 33 && *p <= 60) || (*p >= 62 && *p <= 126)) {
- if (count == 75) {
+ if (can_wrap && count == 75) {
g_string_append (escaped, "=" CRLF " ");
count = 1; /* 1 for space needed for folding */
}
@@ -938,7 +942,7 @@ e_vcard_qp_encode (const gchar *txt)
continue;
}
- if (count >= 73) {
+ if (count >= 73 && can_wrap) {
g_string_append (escaped, "=" CRLF " ");
count = 1; /* 1 for space needed for folding */
}
@@ -950,6 +954,52 @@ e_vcard_qp_encode (const gchar *txt)
return g_string_free (escaped, FALSE);
}
+static gchar *
+e_vcard_qp_decode (const gchar *txt)
+{
+ const gchar *inptr;
+ gchar *decoded, *outptr;
+
+ if (!txt)
+ return NULL;
+
+ decoded = g_malloc (sizeof (gchar) * strlen (txt) + 1);
+
+ outptr = decoded;
+
+ for (inptr = txt; *inptr; inptr++) {
+ gchar c = *inptr;
+
+ if (c == '=' && (inptr[1] == '\r' || inptr[1] == '\n')) {
+ /* soft line-break */
+ if (inptr[2] == '\n')
+ inptr++;
+ inptr++;
+ continue;
+ }
+
+ if (c == '=' && inptr[1] && inptr[2]) {
+ guchar a = toupper (inptr[1]), b = toupper (inptr[2]);
+ if (isxdigit (a) && isxdigit (b)) {
+ *outptr++ = (((a >= 'A' ? a - 'A' + 10 : a - '0') & 0x0f) << 4)
+ | ((b >= 'A' ? b - 'A' + 10 : b - '0') & 0x0f);
+ } else {
+ *outptr++ = '=';
+ *outptr++ = inptr[1];
+ *outptr++ = inptr[2];
+ }
+
+ inptr += 2;
+ } else {
+ *outptr++ = *inptr;
+ }
+ }
+
+ *outptr = '\0';
+
+ return decoded;
+}
+
static GHashTable *
generate_dict_validator (const gchar *words)
{
@@ -1075,7 +1125,7 @@ e_vcard_to_string_vcard_21 (EVCard *evc)
if (encode) {
gchar *escaped_value;
- escaped_value = e_vcard_qp_encode (value);
+ escaped_value = e_vcard_qp_encode (value, TRUE);
g_string_append (attr_str, escaped_value);
g_free (escaped_value);
@@ -1121,6 +1171,7 @@ e_vcard_to_string_vcard_30 (EVCard *evc)
EVCardAttribute *attr = l->data;
GString *attr_str;
glong len;
+ EVCardAttributeParam *quoted_printable_param = NULL;
if (!g_ascii_strcasecmp (attr->name, "VERSION"))
continue;
@@ -1141,6 +1192,18 @@ e_vcard_to_string_vcard_30 (EVCard *evc)
/* handle the parameters */
for (list = attr->params; list; list = list->next) {
EVCardAttributeParam *param = list->data;
+
+ /* quoted-printable encoding was eliminated in 3.0,
+ thus decode the value before saving and remove the param later */
+ if (!quoted_printable_param &&
+ param->values && param->values->data && !param->values->next &&
+ g_ascii_strcasecmp (param->name, "ENCODING") == 0 &&
+ g_ascii_strcasecmp (param->values->data, "quoted-printable") == 0) {
+ quoted_printable_param = param;
+ /* do not store it */
+ continue;
+ }
+
/* 5.8.2:
* param = param-name "=" param-value *("," param-value)
*/
@@ -1148,6 +1211,7 @@ e_vcard_to_string_vcard_30 (EVCard *evc)
g_string_append (attr_str, param->name);
if (param->values) {
g_string_append_c (attr_str, '=');
+
for (v = param->values; v; v = v->next) {
gchar *value = v->data;
gchar *pval = value;
@@ -1189,6 +1253,19 @@ e_vcard_to_string_vcard_30 (EVCard *evc)
gchar *value = v->data;
gchar *escaped_value = NULL;
+ /* values are in quoted-printable encoding, but this cannot be used in vCard 3.0,
+ thus it needs to be converted first */
+ if (quoted_printable_param) {
+ gchar *qp_decoded;
+
+ qp_decoded = e_vcard_qp_decode (value);
+
+ /* replace the actual value with the decoded */
+ g_free (value);
+ value = qp_decoded;
+ v->data = value;
+ }
+
escaped_value = e_vcard_escape_string (value);
g_string_append (attr_str, escaped_value);
@@ -1234,6 +1311,10 @@ e_vcard_to_string_vcard_30 (EVCard *evc)
g_string_append (str, attr_str->str);
g_string_free (attr_str, TRUE);
+
+ /* remove the encoding parameter, to not decode multiple times */
+ if (quoted_printable_param)
+ e_vcard_attribute_remove_param (attr, quoted_printable_param->name);
}
g_string_append (str, "END:VCARD");
@@ -1684,10 +1765,21 @@ e_vcard_attribute_add_value_decoded (EVCardAttribute *attr,
attr->decoded_values = g_list_append (attr->decoded_values, decoded);
break;
}
- case EVC_ENCODING_QP:
- g_warning ("need to implement quoted printable decoding");
+ case EVC_ENCODING_QP: {
+ GString *decoded = g_string_new_len (value, len);
+ gchar *qp_data = e_vcard_qp_encode (decoded->str, FALSE);
+
+ /* make sure the decoded list is up to date */
+ e_vcard_attribute_get_values_decoded (attr);
+
+ d (printf ("qp encoded value: %s\n", qp_data));
+ d (printf ("original length: %d\n", len));
+
+ attr->values = g_list_append (attr->values, qp_data);
+ attr->decoded_values = g_list_append (attr->decoded_values, decoded);
break;
}
+ }
}
/**
@@ -2319,7 +2411,14 @@ e_vcard_attribute_get_values_decoded (EVCardAttribute *attr)
attr->decoded_values = g_list_reverse (attr->decoded_values);
break;
case EVC_ENCODING_QP:
- g_warning ("need to implement quoted printable decoding");
+ for (l = attr->values; l; l = l->next) {
+ gchar *decoded;
+
+ decoded = e_vcard_qp_decode (l->data);
+ attr->decoded_values = g_list_prepend (attr->decoded_values, g_string_new (decoded));
+ g_free (decoded);
+ }
+ attr->decoded_values = g_list_reverse (attr->decoded_values);
break;
}
}
diff --git a/tests/libebook-contacts/test-vcard-parsing.c b/tests/libebook-contacts/test-vcard-parsing.c
index bd85f8731..c32ee04a7 100644
--- a/tests/libebook-contacts/test-vcard-parsing.c
+++ b/tests/libebook-contacts/test-vcard-parsing.c
@@ -1,4 +1,5 @@
#include <libebook/libebook.h>
+#include <string.h>
static gboolean
compare_single_value (EVCard *vcard,
@@ -219,6 +220,163 @@ test_econtact (const gchar *vcard_str)
return TRUE;
}
+static gboolean
+test_vcard_qp_2_1_parsing (const gchar *vcard_str,
+ const gchar *expected_text)
+{
+ EVCard *vcard;
+ EVCardAttribute *attr;
+ gchar *value;
+
+ vcard = e_vcard_new_from_string (vcard_str);
+ g_return_val_if_fail (vcard != NULL, FALSE);
+
+ attr = e_vcard_get_attribute (vcard, "FN");
+ g_return_val_if_fail (attr != NULL, FALSE);
+
+ value = e_vcard_attribute_get_value (attr);
+ g_return_val_if_fail (value != NULL, FALSE);
+
+ g_return_val_if_fail (g_strcmp0 (value, expected_text) == 0, FALSE);
+
+ g_object_unref (vcard);
+ g_free (value);
+
+ return TRUE;
+}
+
+static gboolean
+test_vcard_qp_2_1_saving (const gchar *expected_text)
+{
+ EVCard *vcard;
+ EVCardAttribute *attr;
+ EVCardAttributeParam *param;
+ gchar *str, *encoded_value;
+ GString *decoded;
+
+ vcard = e_vcard_new ();
+ attr = e_vcard_attribute_new (NULL, "FN");
+ param = e_vcard_attribute_param_new ("ENCODING");
+ e_vcard_attribute_param_add_value (param, "quoted-printable");
+ e_vcard_attribute_add_param (attr, param);
+
+ e_vcard_attribute_add_value_decoded (attr, expected_text, strlen (expected_text));
+
+ decoded = e_vcard_attribute_get_value_decoded (attr);
+ g_return_val_if_fail (decoded != NULL, FALSE);
+ g_return_val_if_fail (g_strcmp0 (decoded->str, expected_text) == 0, FALSE);
+ g_string_free (decoded, TRUE);
+
+ encoded_value = e_vcard_attribute_get_value (attr);
+ g_return_val_if_fail (encoded_value != NULL, FALSE);
+ /* it's the encoded value, thus it cannot match */
+ g_return_val_if_fail (g_strcmp0 (encoded_value, expected_text) != 0, FALSE);
+ g_free (encoded_value);
+
+ e_vcard_add_attribute (vcard, attr);
+
+ str = e_vcard_to_string (vcard, EVC_FORMAT_VCARD_21);
+ g_object_unref (vcard);
+
+ g_return_val_if_fail (str != NULL, FALSE);
+
+ vcard = e_vcard_new_from_string (str);
+ g_free (str);
+
+ g_return_val_if_fail (vcard != NULL, FALSE);
+
+ attr = e_vcard_get_attribute (vcard, "FN");
+ g_return_val_if_fail (attr != NULL, FALSE);
+
+ decoded = e_vcard_attribute_get_value_decoded (attr);
+ g_return_val_if_fail (decoded != NULL, FALSE);
+ g_return_val_if_fail (g_strcmp0 (decoded->str, expected_text) == 0, FALSE);
+ g_string_free (decoded, TRUE);
+
+ g_object_unref (vcard);
+
+ return TRUE;
+}
+
+static gboolean
+test_vcard_qp_3_0_saving (const gchar *expected_text)
+{
+ EVCard *vcard;
+ EVCardAttribute *attr;
+ EVCardAttributeParam *param;
+ gchar *str, *value, *encoded_value;
+ GString *decoded;
+
+ vcard = e_vcard_new ();
+ attr = e_vcard_attribute_new (NULL, "FN");
+ param = e_vcard_attribute_param_new ("ENCODING");
+ e_vcard_attribute_param_add_value (param, "quoted-printable");
+ e_vcard_attribute_add_param (attr, param);
+
+ e_vcard_attribute_add_value_decoded (attr, expected_text, strlen (expected_text));
+ e_vcard_add_attribute (vcard, attr);
+
+ decoded = e_vcard_attribute_get_value_decoded (attr);
+ g_return_val_if_fail (decoded != NULL, FALSE);
+ g_return_val_if_fail (g_strcmp0 (decoded->str, expected_text) == 0, FALSE);
+ g_string_free (decoded, TRUE);
+
+ encoded_value = e_vcard_attribute_get_value (attr);
+ g_return_val_if_fail (encoded_value != NULL, FALSE);
+ /* it's the encoded value, thus it cannot match */
+ g_return_val_if_fail (g_strcmp0 (encoded_value, expected_text) != 0, FALSE);
+
+ str = e_vcard_to_string (vcard, EVC_FORMAT_VCARD_30);
+
+ g_object_unref (vcard);
+
+ g_return_val_if_fail (str != NULL, FALSE);
+
+ vcard = e_vcard_new_from_string (str);
+ g_free (str);
+
+ g_return_val_if_fail (vcard != NULL, FALSE);
+
+ attr = e_vcard_get_attribute (vcard, "FN");
+ g_return_val_if_fail (attr != NULL, FALSE);
+
+ decoded = e_vcard_attribute_get_value_decoded (attr);
+ g_return_val_if_fail (decoded != NULL, FALSE);
+ g_return_val_if_fail (g_strcmp0 (decoded->str, expected_text) == 0, FALSE);
+ g_string_free (decoded, TRUE);
+
+ value = e_vcard_attribute_get_value (attr);
+ g_return_val_if_fail (value != NULL, FALSE);
+ /* either base64 or a free form, but not quoted-printable for sure */
+ g_return_val_if_fail (g_strcmp0 (value, encoded_value) != 0, FALSE);
+ g_free (value);
+
+ g_object_unref (vcard);
+ g_free (encoded_value);
+
+ return TRUE;
+}
+
+static void
+test_vcard_quoted_printable (void)
+{
+ const gchar *expected_text = "ActualValue ěščřžýáíéúůóöĚŠČŘŽÝÁÍÉÚŮÓÖ§ "
+ "1234567890 1234567890 1234567890 1234567890 1234567890";
+ const gchar *vcard_2_1_str =
+ "BEGIN:VCARD\r\n"
+ "VERSION:2.1\r\n"
+ "FN;ENCODING=quoted-printable:ActualValue=20=C4=9B=C5=A1"
+ "=C4=8D=C5=99=C5=BE=C3=BD=C3=A1=C3=AD=C3=A9=C3=BA=C5=AF=C3"
+ "=B3=C3=B6=C4=9A=C5=A0=C4=8C=C5=98=C5=BD=C3=9D=C3=81=C3=8D"
+ "=C3=89=C3=9A=C5=AE=C3=93=C3=96=C2=A7=201234567890=2012345"
+ "67890=201234567890=201234567890=201234567890\r\n"
+ "END:VCARD\r\n";
+
+ g_assert (test_vcard_qp_2_1_parsing (vcard_2_1_str, expected_text));
+ g_assert (test_vcard_qp_2_1_saving (expected_text));
+ g_assert (test_vcard_qp_3_0_saving (expected_text));
+}
+
static const gchar *test_vcard_no_uid_str =
"BEGIN:VCARD\r\n"
"VERSION:3.0\r\n"
@@ -273,6 +431,7 @@ main (gint argc,
g_test_add_func ("/Parsing/VCard/WithoutUID", test_vcard_without_uid);
g_test_add_func ("/Parsing/VCard/WithUID", test_contact_with_uid);
g_test_add_func ("/Parsing/VCard/WithoutUID", test_contact_without_uid);
+ g_test_add_func ("/Parsing/VCard/QuotedPrintable", test_vcard_quoted_printable);
return g_test_run ();
}