diff --git a/addressbook/libebook-contacts/e-book-query.c b/addressbook/libebook-contacts/e-book-query.c
index fd507ab..5008626 100644
--- a/addressbook/libebook-contacts/e-book-query.c
+++ b/addressbook/libebook-contacts/e-book-query.c
@@ -366,31 +366,35 @@ e_book_query_ref (EBookQuery *q)
 	return q;
 }
 
+typedef EBookQuery * (* EBookQueryNAry) (gint nqs,
+                                         EBookQuery **qs,
+                                         gboolean unref);
+
 static ESExpResult *
-func_and (struct _ESExp *f,
-          gint argc,
-          struct _ESExpResult **argv,
-          gpointer data)
+func_n_ary (EBookQueryNAry make_query,
+            struct _ESExp *f,
+            gint argc,
+            struct _ESExpResult **argv,
+            gpointer data)
 {
 	GList **list = data;
 	ESExpResult *r;
 	EBookQuery **qs;
 
 	if (argc > 0) {
-		gint i;
+		gint i, j;
 
 		qs = g_new0 (EBookQuery *, argc);
 
-		for (i = 0; i < argc; i++) {
+		for (i = 0, j = argc - 1; i < argc; i++, j--) {
 			GList *list_head = *list;
 			if (!list_head)
 				break;
-			qs[i] = list_head->data;
+			qs[j] = list_head->data;
 			*list = g_list_delete_link(*list, list_head);
 		}
 
-		*list = g_list_prepend(*list,
-				       e_book_query_and (argc, qs, TRUE));
+		*list = g_list_prepend(*list, make_query (argc, qs, TRUE));
 
 		g_free (qs);
 	}
@@ -402,38 +406,21 @@ func_and (struct _ESExp *f,
 }
 
 static ESExpResult *
+func_and (struct _ESExp *f,
+          gint argc,
+          struct _ESExpResult **argv,
+          gpointer data)
+{
+	return func_n_ary (e_book_query_and, f, argc, argv, data);
+}
+
+static ESExpResult *
 func_or (struct _ESExp *f,
          gint argc,
          struct _ESExpResult **argv,
          gpointer data)
 {
-	GList **list = data;
-	ESExpResult *r;
-	EBookQuery **qs;
-
-	if (argc > 0) {
-		gint i;
-
-		qs = g_new0 (EBookQuery *, argc);
-
-		for (i = 0; i < argc; i++) {
-			GList *list_head = *list;
-			if (!list_head)
-				break;
-			qs[i] = list_head->data;
-			*list = g_list_delete_link(*list, list_head);
-		}
-
-		*list = g_list_prepend(*list,
-				       e_book_query_or (argc, qs, TRUE));
-
-		g_free (qs);
-	}
-
-	r = e_sexp_result_new (f, ESEXP_RES_BOOL);
-	r->value.boolean = FALSE;
-
-	return r;
+	return func_n_ary (e_book_query_or, f, argc, argv, data);
 }
 
 static ESExpResult *
@@ -458,10 +445,11 @@ func_not (struct _ESExp *f,
 }
 
 static ESExpResult *
-func_contains (struct _ESExp *f,
-               gint argc,
-               struct _ESExpResult **argv,
-               gpointer data)
+func_field_test (EBookQueryTest op,
+                 struct _ESExp *f,
+                 gint argc,
+                 struct _ESExpResult **argv,
+                 gpointer data)
 {
 	GList **list = data;
 	ESExpResult *r;
@@ -471,22 +459,21 @@ func_contains (struct _ESExp *f,
 	    && argv[1]->type == ESEXP_RES_STRING) {
 		gchar *propname = argv[0]->value.string;
 		gchar *str = argv[1]->value.string;
+		EBookQuery *q;
 
-		if (!strcmp (propname, "x-evolution-any-field")) {
-			*list = g_list_prepend (*list, e_book_query_any_field_contains (str));
-		}
-		else {
+		if (op == E_BOOK_QUERY_CONTAINS
+		    && strcmp (propname, "x-evolution-any-field") == 0) {
+			q = e_book_query_any_field_contains (str);
+		} else {
 			EContactField field = e_contact_field_id (propname);
 
 			if (field)
-				*list = g_list_prepend (*list, e_book_query_field_test (field,
-											E_BOOK_QUERY_CONTAINS,
-											str));
+				q = e_book_query_field_test (field, op, str);
 			else
-				*list = g_list_prepend (*list, e_book_query_vcard_field_test (propname,
-											E_BOOK_QUERY_CONTAINS,
-											str));
+				q = e_book_query_vcard_field_test (propname, op, str);
 		}
+
+		*list = g_list_prepend (*list, q);
 	}
 
 	r = e_sexp_result_new (f, ESEXP_RES_BOOL);
@@ -496,35 +483,21 @@ func_contains (struct _ESExp *f,
 }
 
 static ESExpResult *
+func_contains (struct _ESExp *f,
+               gint argc,
+               struct _ESExpResult **argv,
+               gpointer data)
+{
+	return func_field_test (E_BOOK_QUERY_CONTAINS, f, argc, argv, data);
+}
+
+static ESExpResult *
 func_is (struct _ESExp *f,
          gint argc,
          struct _ESExpResult **argv,
          gpointer data)
 {
-	GList **list = data;
-	ESExpResult *r;
-
-	if (argc == 2
-	    && argv[0]->type == ESEXP_RES_STRING
-	    && argv[1]->type == ESEXP_RES_STRING) {
-		gchar *propname = argv[0]->value.string;
-		gchar *str = argv[1]->value.string;
-		EContactField field = e_contact_field_id (propname);
-
-		if (field)
-			*list = g_list_prepend (*list, e_book_query_field_test (field,
-										E_BOOK_QUERY_IS,
-										str));
-		else
-			*list = g_list_prepend (*list, e_book_query_vcard_field_test (propname,
-										E_BOOK_QUERY_IS,
-										str));
-	}
-
-	r = e_sexp_result_new (f, ESEXP_RES_BOOL);
-	r->value.boolean = FALSE;
-
-	return r;
+	return func_field_test (E_BOOK_QUERY_IS, f, argc, argv, data);
 }
 
 static ESExpResult *
@@ -533,30 +506,7 @@ func_beginswith (struct _ESExp *f,
                  struct _ESExpResult **argv,
                  gpointer data)
 {
-	GList **list = data;
-	ESExpResult *r;
-
-	if (argc == 2
-	    && argv[0]->type == ESEXP_RES_STRING
-	    && argv[1]->type == ESEXP_RES_STRING) {
-		gchar *propname = argv[0]->value.string;
-		gchar *str = argv[1]->value.string;
-		EContactField field = e_contact_field_id (propname);
-
-		if (field)
-			*list = g_list_prepend (*list, e_book_query_field_test (field,
-										E_BOOK_QUERY_BEGINS_WITH,
-										str));
-		else
-			*list = g_list_prepend (*list, e_book_query_vcard_field_test (propname,
-										E_BOOK_QUERY_BEGINS_WITH,
-										str));
-	}
-
-	r = e_sexp_result_new (f, ESEXP_RES_BOOL);
-	r->value.boolean = FALSE;
-
-	return r;
+	return func_field_test (E_BOOK_QUERY_BEGINS_WITH, f, argc, argv, data);
 }
 
 static ESExpResult *
@@ -565,30 +515,34 @@ func_endswith (struct _ESExp *f,
                struct _ESExpResult **argv,
                gpointer data)
 {
-	GList **list = data;
-	ESExpResult *r;
-
-	if (argc == 2
-	    && argv[0]->type == ESEXP_RES_STRING
-	    && argv[1]->type == ESEXP_RES_STRING) {
-		gchar *propname = argv[0]->value.string;
-		gchar *str = argv[1]->value.string;
-		EContactField field = e_contact_field_id (propname);
+	return func_field_test (E_BOOK_QUERY_ENDS_WITH, f, argc, argv, data);
+}
 
-		if (field)
-			*list = g_list_prepend (*list, e_book_query_field_test (field,
-										E_BOOK_QUERY_ENDS_WITH,
-										str));
-		else
-			*list = g_list_prepend (*list, e_book_query_vcard_field_test (propname,
-										E_BOOK_QUERY_ENDS_WITH,
-										str));
-	}
+static ESExpResult *
+func_eqphone (struct _ESExp *f,
+              gint argc,
+              struct _ESExpResult **argv,
+              gpointer data)
+{
+	return func_field_test (E_BOOK_QUERY_EQUALS_PHONE_NUMBER, f, argc, argv, data);
+}
 
-	r = e_sexp_result_new (f, ESEXP_RES_BOOL);
-	r->value.boolean = FALSE;
+static ESExpResult *
+func_eqphone_national (struct _ESExp *f,
+                       gint argc,
+                       struct _ESExpResult **argv,
+                       gpointer data)
+{
+	return func_field_test (E_BOOK_QUERY_EQUALS_NATIONAL_PHONE_NUMBER, f, argc, argv, data);
+}
 
-	return r;
+static ESExpResult *
+func_eqphone_short (struct _ESExp *f,
+                    gint argc,
+                    struct _ESExpResult **argv,
+                    gpointer data)
+{
+	return func_field_test (E_BOOK_QUERY_EQUALS_SHORT_PHONE_NUMBER, f, argc, argv, data);
 }
 
 static ESExpResult *
@@ -631,6 +585,9 @@ static const struct {
 	{ "is", func_is, 0 },
 	{ "beginswith", func_beginswith, 0 },
 	{ "endswith", func_endswith, 0 },
+	{ "eqphone", func_eqphone, 0 },
+	{ "eqphone_national", func_eqphone_national, 0 },
+	{ "eqphone_short", func_eqphone_short, 0 },
 	{ "exists", func_exists, 0 },
 };
 
@@ -665,24 +622,21 @@ e_book_query_from_string (const gchar *query_string)
 	}
 
 	e_sexp_input_text (sexp, query_string, strlen (query_string));
-	e_sexp_parse (sexp);
+
+	if (e_sexp_parse (sexp)) {
+		g_warning ("%s: Error in parsing: %s", G_STRFUNC, sexp->error);
+		return NULL;
+	}
 
 	r = e_sexp_eval (sexp);
 
 	e_sexp_result_free (sexp, r);
 	e_sexp_unref (sexp);
 
-	if (list) {
-		if (list->next) {
-			g_warning ("conversion to EBookQuery");
-			retval = NULL;
-			g_list_foreach (list, (GFunc) e_book_query_unref, NULL);
-		}
-		else {
-			retval = list->data;
-		}
-	}
-	else {
+	if (list && list->next == NULL) {
+		retval = list->data;
+	} else {
+		g_list_foreach (list, (GFunc) e_book_query_unref, NULL);
 		g_warning ("conversion to EBookQuery failed");
 		retval = NULL;
 	}
@@ -691,6 +645,29 @@ e_book_query_from_string (const gchar *query_string)
 	return retval;
 }
 
+static const char *
+field_test_name (EBookQueryTest field_test)
+{
+	switch (field_test) {
+	case E_BOOK_QUERY_IS:
+		return "is";
+	case E_BOOK_QUERY_CONTAINS:
+		return "contains";
+	case E_BOOK_QUERY_BEGINS_WITH:
+		return "beginswith";
+	case E_BOOK_QUERY_ENDS_WITH:
+		return "endswith";
+	case E_BOOK_QUERY_EQUALS_PHONE_NUMBER:
+		return "eqphone";
+	case E_BOOK_QUERY_EQUALS_NATIONAL_PHONE_NUMBER:
+		return "eqphone_national";
+	case E_BOOK_QUERY_EQUALS_SHORT_PHONE_NUMBER:
+		return "eqphone_short";
+	}
+
+	g_assert_not_reached ();
+}
+
 /**
  * e_book_query_to_string:
  * @q: an #EBookQuery
@@ -707,7 +684,6 @@ e_book_query_to_string (EBookQuery *q)
 	GString *encoded = g_string_new ("");
 	gint i;
 	gchar *s = NULL;
-	const gchar *cs;
 
 	switch (q->type) {
 	case E_BOOK_QUERY_TYPE_AND:
@@ -741,21 +717,11 @@ e_book_query_to_string (EBookQuery *q)
 		}
 		break;
 	case E_BOOK_QUERY_TYPE_FIELD_TEST:
-		switch (q->query.field_test.test) {
-		case E_BOOK_QUERY_IS: cs = "is"; break;
-		case E_BOOK_QUERY_CONTAINS: cs = "contains"; break;
-		case E_BOOK_QUERY_BEGINS_WITH: cs = "beginswith"; break;
-		case E_BOOK_QUERY_ENDS_WITH: cs = "endswith"; break;
-		default:
-			g_assert_not_reached ();
-			break;
-		}
-
 		e_sexp_encode_string (encoded, q->query.field_test.value);
 
 		g_string_append_printf (
 			str, "%s \"%s\" %s",
-			cs,
+			field_test_name (q->query.field_test.test),
 			q->query.field_test.field_name,
 			encoded->str);
 		break;
diff --git a/addressbook/libebook-contacts/e-book-query.h b/addressbook/libebook-contacts/e-book-query.h
index 4bd235c..0025617 100644
--- a/addressbook/libebook-contacts/e-book-query.h
+++ b/addressbook/libebook-contacts/e-book-query.h
@@ -18,7 +18,11 @@ typedef enum {
   E_BOOK_QUERY_IS,
   E_BOOK_QUERY_CONTAINS,
   E_BOOK_QUERY_BEGINS_WITH,
-  E_BOOK_QUERY_ENDS_WITH
+  E_BOOK_QUERY_ENDS_WITH,
+
+  E_BOOK_QUERY_EQUALS_PHONE_NUMBER,
+  E_BOOK_QUERY_EQUALS_NATIONAL_PHONE_NUMBER,
+  E_BOOK_QUERY_EQUALS_SHORT_PHONE_NUMBER,
 
   /*
     Consider these "coming soon".
diff --git a/addressbook/libedata-book/e-book-backend-sexp.c b/addressbook/libedata-book/e-book-backend-sexp.c
index 30ca998..3fac3b0 100644
--- a/addressbook/libedata-book/e-book-backend-sexp.c
+++ b/addressbook/libedata-book/e-book-backend-sexp.c
@@ -18,10 +18,12 @@
  * 02110-1301, USA.
  */
 
-#include <string.h>
-
 #include "e-book-backend-sexp.h"
 
+#include <libedataserver/libedataserver.h>
+
+#include <string.h>
+
 #define E_BOOK_BACKEND_SEXP_GET_PRIVATE(obj) \
 	(G_TYPE_INSTANCE_GET_PRIVATE \
 	((obj), E_TYPE_BOOK_BACKEND_SEXP, EBookBackendSExpPrivate))
@@ -29,6 +31,7 @@
 G_DEFINE_TYPE (EBookBackendSExp, e_book_backend_sexp, G_TYPE_OBJECT)
 
 typedef struct _SearchContext SearchContext;
+typedef gboolean (*CompareFunc) (const gchar *, const gchar *);
 
 struct _EBookBackendSExpPrivate {
 	ESExp *search_sexp;
@@ -42,8 +45,7 @@ struct _SearchContext {
 static gboolean
 compare_im (EContact *contact,
             const gchar *str,
-            gchar * (*compare) (const gchar *,
-                                const gchar *),
+            CompareFunc compare,
             EContactField im_field)
 {
 	GList    *aims, *l;
@@ -68,8 +70,7 @@ compare_im (EContact *contact,
 static gboolean
 compare_im_aim (EContact *contact,
                 const gchar *str,
-                gchar * (*compare) (const gchar *,
-                                    const gchar *))
+                CompareFunc compare)
 {
 	return compare_im (contact, str, compare, E_CONTACT_IM_AIM);
 }
@@ -77,8 +78,7 @@ compare_im_aim (EContact *contact,
 static gboolean
 compare_im_msn (EContact *contact,
                 const gchar *str,
-                gchar * (*compare) (const gchar *,
-                                    const gchar *))
+                CompareFunc compare)
 {
 	return compare_im (contact, str, compare, E_CONTACT_IM_MSN);
 }
@@ -86,8 +86,7 @@ compare_im_msn (EContact *contact,
 static gboolean
 compare_im_skype (EContact *contact,
                   const gchar *str,
-                  gchar * (*compare) (const gchar *,
-                                      const gchar *))
+                  CompareFunc compare)
 {
 	return compare_im (contact, str, compare, E_CONTACT_IM_SKYPE);
 }
@@ -95,8 +94,7 @@ compare_im_skype (EContact *contact,
 static gboolean
 compare_im_google_talk (EContact *contact,
                         const gchar *str,
-                        gchar * (*compare) (const gchar *,
-                                            const gchar *))
+                        CompareFunc compare)
 {
 	return compare_im (contact, str, compare, E_CONTACT_IM_GOOGLE_TALK);
 }
@@ -104,8 +102,7 @@ compare_im_google_talk (EContact *contact,
 static gboolean
 compare_im_icq (EContact *contact,
                 const gchar *str,
-                gchar * (*compare) (const gchar *,
-                                    const gchar *))
+                CompareFunc compare)
 {
 	return compare_im (contact, str, compare, E_CONTACT_IM_ICQ);
 }
@@ -113,8 +110,7 @@ compare_im_icq (EContact *contact,
 static gboolean
 compare_im_yahoo (EContact *contact,
                   const gchar *str,
-                  gchar *(*compare) (const gchar *,
-                                     const gchar *))
+                  CompareFunc compare)
 {
 	return compare_im (contact, str, compare, E_CONTACT_IM_YAHOO);
 }
@@ -122,8 +118,7 @@ compare_im_yahoo (EContact *contact,
 static gboolean
 compare_im_gadugadu (EContact *contact,
                      const gchar *str,
-                     gchar * (*compare) (const gchar *,
-                                         const gchar *))
+                     CompareFunc compare)
 {
 	return compare_im (contact, str, compare, E_CONTACT_IM_GADUGADU);
 }
@@ -131,8 +126,7 @@ compare_im_gadugadu (EContact *contact,
 static gboolean
 compare_im_jabber (EContact *contact,
                    const gchar *str,
-                   gchar * (*compare) (const gchar *,
-                                       const gchar *))
+                   CompareFunc compare)
 {
 	return compare_im (contact, str, compare, E_CONTACT_IM_JABBER);
 }
@@ -140,8 +134,7 @@ compare_im_jabber (EContact *contact,
 static gboolean
 compare_im_groupwise (EContact *contact,
                       const gchar *str,
-                      gchar * (*compare) (const gchar *,
-                                          const gchar *))
+                      CompareFunc compare)
 {
 	return compare_im (contact, str, compare, E_CONTACT_IM_GROUPWISE);
 }
@@ -149,8 +142,7 @@ compare_im_groupwise (EContact *contact,
 static gboolean
 compare_email (EContact *contact,
                const gchar *str,
-               gchar * (*compare) (const gchar *,
-                                   const gchar *))
+               CompareFunc compare)
 {
 	gboolean rv = FALSE;
 	GList *list, *l;
@@ -174,8 +166,7 @@ compare_email (EContact *contact,
 static gboolean
 compare_phone (EContact *contact,
                const gchar *str,
-               gchar * (*compare) (const gchar *,
-                                   const gchar *))
+               CompareFunc compare)
 {
 	GList *list, *l;
 	gboolean rv = FALSE;
@@ -199,8 +190,7 @@ compare_phone (EContact *contact,
 static gboolean
 compare_name (EContact *contact,
               const gchar *str,
-              gchar * (*compare) (const gchar *,
-                                  const gchar *))
+              CompareFunc compare)
 {
 	const gchar *name;
 
@@ -226,8 +216,7 @@ compare_name (EContact *contact,
 static gboolean
 compare_photo_uri (EContact *contact,
                    const gchar *str,
-                   gchar * (*compare) (const gchar *,
-                                       const gchar *))
+                   CompareFunc compare)
 {
 	EContactPhoto *photo;
 	gboolean ret_val = FALSE;
@@ -248,8 +237,7 @@ compare_photo_uri (EContact *contact,
 static gboolean
 compare_address (EContact *contact,
                  const gchar *str,
-                 gchar * (*compare) (const gchar *,
-                                     const gchar *))
+                 CompareFunc compare)
 {
 
 	gint i;
@@ -280,8 +268,7 @@ compare_address (EContact *contact,
 static gboolean
 compare_category (EContact *contact,
                   const gchar *str,
-                  gchar * (*compare) (const gchar *,
-                                      const gchar *))
+                  CompareFunc compare)
 {
 	GList *categories;
 	GList *iterator;
@@ -307,8 +294,7 @@ compare_category (EContact *contact,
 static gboolean
 compare_date (EContactDate *date,
               const gchar *str,
-              gchar * (*compare) (const gchar *,
-                                  const gchar *))
+              CompareFunc compare)
 {
 	gchar *date_str = e_contact_date_to_string (date);
 	gboolean ret_val = FALSE;
@@ -334,8 +320,7 @@ static struct prop_info {
 	enum prop_type prop_type;
 	gboolean (*list_compare) (EContact *contact,
 				  const gchar *str,
-				  gchar * (*compare) (const gchar *,
-						      const gchar *));
+				  CompareFunc compare);
 
 } prop_info_table[] = {
 #define NORMAL_PROP(f,q) {f, q, PROP_TYPE_NORMAL, NULL}
@@ -389,8 +374,7 @@ entry_compare (SearchContext *ctx,
                struct _ESExp *f,
                gint argc,
                struct _ESExpResult **argv,
-               gchar * (*compare) (const gchar *,
-                                   const gchar *))
+               CompareFunc compare)
 {
 	ESExpResult *r;
 	gint truth = FALSE;
@@ -517,7 +501,7 @@ contains_helper_free_word (gpointer data,
 	}
 }
 
-static gchar *
+static gboolean
 try_contains_word (const gchar *s1,
                    GSList *word)
 {
@@ -526,11 +510,11 @@ try_contains_word (const gchar *s1,
 	GString *w;
 
 	if (s1 == NULL)
-		return NULL;
+		return FALSE;
 	if (word == NULL)
-		return (gchar *) s1; /* previous was last word */
+		return TRUE; /* previous was last word */
 	if (word->data == NULL)
-		return NULL; /* illegal structure */
+		return FALSE; /* illegal structure */
 
 	w = word->data;
 	first_w_char = g_utf8_get_char (w->str);
@@ -555,20 +539,20 @@ try_contains_word (const gchar *s1,
 				/* we read whole word and no illegal character has been found */
 				if (word->next == NULL ||
 				    try_contains_word (e_util_unicode_get_utf8 (o, &unival), word->next)) {
-					return (gchar *) o;
+					return TRUE;
 				}
 			}
 		}
 		o = p;
 	}
 
-	return NULL;
+	return FALSE;
 }
 
 /* first space between words is treated as wildcard character;
  * we are looking for s2 in s1, so s2 will be breaked into words
 */
-static gchar *
+static gboolean
 contains_helper (const gchar *s1,
                  const gchar *s2)
 {
@@ -579,25 +563,25 @@ contains_helper (const gchar *s1,
 	gboolean have_nonspace;
 	gboolean have_space;
 	GString *last_word, *w;
-	gchar *res = NULL;
+	gboolean res;
 	gunichar unich;
 	glong len1, len2;
 
 	if (!s2)
-		return NULL;
+		return FALSE;
 
 	/* the initial word contains an empty string for sure */
 	if (!*s2)
-		return (gchar *) s1;
+		return TRUE;
 
 	s1uni = e_util_utf8_normalize (s1);
 	if (s1uni == NULL)
-		return NULL;
+		return FALSE;
 
 	s2uni = e_util_utf8_normalize (s2);
 	if (s2uni == NULL) {
 		g_free (s1uni);
-		return NULL;
+		return FALSE;
 	}
 
 	len1 = g_utf8_strlen (s1uni, -1);
@@ -608,9 +592,9 @@ contains_helper (const gchar *s1,
 
 		/* both are empty strings */
 		if (len1 == len2)
-			return (gchar *) s1;
+			return TRUE;
 
-		return NULL;
+		return FALSE;
 	}
 
 	/* breaking s2 into words */
@@ -671,19 +655,18 @@ func_contains (struct _ESExp *f,
 	return entry_compare (ctx, f, argc, argv, contains_helper);
 }
 
-static gchar *
+static gboolean
 is_helper (const gchar *ps1,
            const gchar *ps2)
 {
-	gchar *s1, *s2, *res;
+	gchar *s1, *s2;
+	gboolean res = FALSE;
 
 	s1 = e_util_utf8_remove_accents (ps1);
 	s2 = e_util_utf8_remove_accents (ps2);
 
 	if (!e_util_utf8_strcasecmp (s1, s2))
-		res = (gchar *) ps1;
-	else
-		res = NULL;
+		res = TRUE;
 
 	g_free (s1);
 	g_free (s2);
@@ -702,20 +685,18 @@ func_is (struct _ESExp *f,
 	return entry_compare (ctx, f, argc, argv, is_helper);
 }
 
-static gchar *
+static gboolean
 endswith_helper (const gchar *ps1,
                  const gchar *ps2)
 {
 	gchar *s1 = e_util_utf8_remove_accents (ps1);
 	gchar *s2 = e_util_utf8_remove_accents (ps2);
-	gchar *res;
+	gboolean res = FALSE;
 	glong s1len = g_utf8_strlen (s1, -1);
 	glong s2len = g_utf8_strlen (s2, -1);
 
-	if (s1len < s2len)
-		res = NULL;
-	else
-		res = (gchar *) e_util_utf8_strstrcase (g_utf8_offset_to_pointer (s1, s1len - s2len), s2);
+	if (s1len >= s2len)
+		res = TRUE;
 
 	g_free (s1);
 	g_free (s2);
@@ -734,19 +715,18 @@ func_endswith (struct _ESExp *f,
 	return entry_compare (ctx, f, argc, argv, endswith_helper);
 }
 
-static gchar *
+static gboolean
 beginswith_helper (const gchar *ps1,
                    const gchar *ps2)
 {
-	gchar *p, *res;
+	gchar *p;
+	gboolean res = FALSE;
 	gchar *s1 = e_util_utf8_remove_accents (ps1);
 	gchar *s2 = e_util_utf8_remove_accents (ps2);
 
 	if ((p = (gchar *) e_util_utf8_strstrcase (s1, s2))
 	    && (p == s1))
-		res = (gchar *) ps1;
-	else
-		res = NULL;
+		res = TRUE;
 
 	g_free (s1);
 	g_free (s2);
@@ -765,15 +745,82 @@ func_beginswith (struct _ESExp *f,
 	return entry_compare (ctx, f, argc, argv, beginswith_helper);
 }
 
-static gchar *
+static gboolean
+eqphone_helper (const gchar *ps1,
+                const gchar *ps2,
+                EPhoneNumberMatch required_match)
+{
+	const EPhoneNumberMatch actual_match =
+		e_phone_number_compare_strings (ps1, ps2, NULL);
+
+	return actual_match >= E_PHONE_NUMBER_MATCH_EXACT
+		&& actual_match <= required_match;
+}
+
+static gboolean
+eqphone_exact_helper (const gchar *ps1,
+                      const gchar *ps2)
+{
+	return eqphone_helper (ps1, ps2, E_PHONE_NUMBER_MATCH_EXACT);
+}
+
+static gboolean
+eqphone_national_helper (const gchar *ps1,
+                         const gchar *ps2)
+{
+	return eqphone_helper (ps1, ps2, E_PHONE_NUMBER_MATCH_NATIONAL);
+}
+
+static gboolean
+eqphone_short_helper (const gchar *ps1,
+                      const gchar *ps2)
+{
+	return eqphone_helper (ps1, ps2, E_PHONE_NUMBER_MATCH_SHORT);
+}
+
+static ESExpResult *
+func_eqphone (struct _ESExp *f,
+              gint argc,
+              struct _ESExpResult **argv,
+              gpointer data)
+{
+	SearchContext *ctx = data;
+
+	return entry_compare (ctx, f, argc, argv, eqphone_exact_helper);
+}
+
+static ESExpResult *
+func_eqphone_national (struct _ESExp *f,
+                       gint argc,
+                       struct _ESExpResult **argv,
+                       gpointer data)
+{
+	SearchContext *ctx = data;
+
+	return entry_compare (ctx, f, argc, argv, eqphone_national_helper);
+}
+
+static ESExpResult *
+func_eqphone_short (struct _ESExp *f,
+                    gint argc,
+                    struct _ESExpResult **argv,
+                    gpointer data)
+{
+	SearchContext *ctx = data;
+
+	return entry_compare (ctx, f, argc, argv, eqphone_short_helper);
+}
+
+static gboolean
 exists_helper (const gchar *ps1,
                const gchar *ps2)
 {
-	gchar *res;
+	gboolean res = FALSE;
 	gchar *s1 = e_util_utf8_remove_accents (ps1);
 	gchar *s2 = e_util_utf8_remove_accents (ps2);
 
-	res = (gchar *) e_util_utf8_strstrcase (s1, s2);
+	if (e_util_utf8_strstrcase (s1, s2))
+		res = TRUE;
 
 	g_free (s1);
 	g_free (s2);
@@ -915,6 +962,9 @@ static struct {
 	{ "is", func_is, 0 },
 	{ "beginswith", func_beginswith, 0 },
 	{ "endswith", func_endswith, 0 },
+	{ "eqphone", func_eqphone, 0 },
+	{ "eqphone_national", func_eqphone_national, 0 },
+	{ "eqphone_short", func_eqphone_short, 0 },
 	{ "exists", func_exists, 0 },
 	{ "exists_vcard", func_exists_vcard, 0 },
 };
@@ -1013,6 +1063,7 @@ e_book_backend_sexp_new (const gchar *text)
 	esexp_error = e_sexp_parse (sexp->priv->search_sexp);
 
 	if (esexp_error == -1) {
+		g_warning ("%s: Error in parsing: %s", G_STRFUNC, sexp->priv->search_sexp->error);
 		g_object_unref (sexp);
 		sexp = NULL;
 	}
diff --git a/calendar/libedata-cal/e-cal-backend-sexp.c b/calendar/libedata-cal/e-cal-backend-sexp.c
index db9c9e4..e48be23 100644
--- a/calendar/libedata-cal/e-cal-backend-sexp.c
+++ b/calendar/libedata-cal/e-cal-backend-sexp.c
@@ -1626,6 +1626,7 @@ e_cal_backend_sexp_new (const gchar *text)
 	esexp_error = e_sexp_parse (sexp->priv->search_sexp);
 
 	if (esexp_error == -1) {
+		g_warning ("%s: Error in parsing: %s", G_STRFUNC, sexp->priv->search_sexp->error);
 		g_object_unref (sexp);
 		sexp = NULL;
 	}
diff --git a/configure.ac b/configure.ac
index 780667a..1c97768 100644
--- a/configure.ac
+++ b/configure.ac
@@ -216,6 +216,7 @@ fi
 
 AC_PROG_CC
 AC_PROG_CPP
+AC_PROG_CXX
 AC_C_INLINE
 AM_PROG_CC_C_O
 AC_PROG_INSTALL
@@ -595,18 +596,11 @@ if test "x$enable_backtraces" = xyes; then
 	fi
 fi
 
-dnl **********************************
-dnl Check for nl_langinfo and CODESET
-dnl **********************************
-AC_MSG_CHECKING([for nl_langinfo (CODESET)])
-AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
-	[[#include <langinfo.h>]],
-	[[char *codeset = nl_langinfo (CODESET);]]
-	)],[ac_cv_langinfo_codeset=yes],[ac_cv_langinfo_codeset=no])
-if test "x$ac_cv_langinfo_codeset" = "xyes"; then
-	AC_DEFINE(HAVE_CODESET, 1, [Have nl_langinfo (CODESET)])
-fi
-AC_MSG_RESULT([$ac_cv_langinfo_codeset])
+dnl ******************************
+dnl Check for nl_langinfo features
+dnl ******************************
+EVO_CHECK_LANGINFO([CODESET])
+EVO_CHECK_LANGINFO([_NL_ADDRESS_COUNTRY_AB2])
 
 dnl *******************************************************
 dnl Check to see if strftime supports the use of %l and %k
@@ -1239,6 +1233,11 @@ else
 fi
 
 dnl ******************************
+dnl libphonenumber support
+dnl ******************************
+EVO_PHONENUMBER_SUPPORT
+
+dnl ******************************
 dnl GObject marshalling
 dnl ******************************
 AM_PATH_GLIB_2_0
@@ -1654,6 +1653,7 @@ echo "
 	Kerberos 5:		$msg_krb5
 	SMIME support:		$msg_smime
 	IPv6 support:		$msg_ipv6
+	Phone number support:	$msg_phonenumber
 	Dot Locking:		$msg_dot
 	File Locking:		$msg_file
 	Large files:		$enable_largefile
diff --git a/docs/reference/libedataserver/libedataserver-docs.sgml b/docs/reference/libedataserver/libedataserver-docs.sgml
index 54439b5..c89c9e9 100644
--- a/docs/reference/libedataserver/libedataserver-docs.sgml
+++ b/docs/reference/libedataserver/libedataserver-docs.sgml
@@ -54,6 +54,7 @@
     <xi:include href="xml/e-flag.xml"/>
     <xi:include href="xml/e-memory.xml"/>
     <xi:include href="xml/e-operation-pool.xml"/>
+    <xi:include href="xml/e-phone-utils.xml"/>
     <xi:include href="xml/e-proxy.xml"/>
     <xi:include href="xml/e-sexp.xml"/>
     <xi:include href="xml/e-time-utils.xml"/>
diff --git a/docs/reference/libedataserver/libedataserver-sections.txt b/docs/reference/libedataserver/libedataserver-sections.txt
index a963209..c395cd5 100644
--- a/docs/reference/libedataserver/libedataserver-sections.txt
+++ b/docs/reference/libedataserver/libedataserver-sections.txt
@@ -215,6 +215,24 @@ e_operation_pool_push
 </SECTION>
 
 <SECTION>
+<FILE>e-phone-utils</FILE>
+EPhoneNumber
+EPhoneNumberError
+EPhoneNumberFormat
+e_phone_number_from_string
+e_phone_number_to_string
+e_phone_number_compare
+e_phone_number_compare_strings
+e_phone_number_copy
+e_phone_number_free
+<SUBSECTION Standard>
+E_PHONE_NUMBER_ERROR
+E_TYPE_PHONE_NUMBER
+e_phone_number_error_quark
+e_phone_number_get_type
+</SECTION>
+
+<SECTION>
 <FILE>e-proxy</FILE>
 <TITLE>EProxy</TITLE>
 EProxy
diff --git a/libedataserver/Makefile.am b/libedataserver/Makefile.am
index 44111a0..20d8c8e 100644
--- a/libedataserver/Makefile.am
+++ b/libedataserver/Makefile.am
@@ -57,6 +57,7 @@ libedataserver_1_2_la_SOURCES =		\
 	e-list-iterator.c		\
 	e-memory.c			\
 	e-operation-pool.c		\
+	e-phone-utils.cpp		\
 	e-proxy.c			\
 	e-sexp.c			\
 	e-source.c			\
@@ -112,6 +113,11 @@ libedataserver_1_2_la_LDFLAGS = \
 	$(CODE_COVERAGE_LDFLAGS) \
 	$(NULL)
 
+if ENABLE_PHONENUMBER
+libedataserver_1_2_la_CPPFLAGS += $(PHONENUMBER_INCLUDES)
+libedataserver_1_2_la_LIBADD += $(PHONENUMBER_LIBS)
+endif ENABLE_PHONENUMBER
+
 libedataserverincludedir = $(privincludedir)/libedataserver
 
 libedataserverinclude_HEADERS =		\
@@ -127,6 +133,7 @@ libedataserverinclude_HEADERS =		\
 	e-list-iterator.h		\
 	e-memory.h			\
 	e-operation-pool.h		\
+	e-phone-utils.h			\
 	e-proxy.h			\
 	e-sexp.h			\
 	e-source.h			\
diff --git a/libedataserver/e-phone-utils.cpp b/libedataserver/e-phone-utils.cpp
new file mode 100644
index 0000000..d2ad7b6
--- /dev/null
+++ b/libedataserver/e-phone-utils.cpp
@@ -0,0 +1,313 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright Copyright (C) 2012 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser 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 Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * Author: Mathias Hasselmann <mathias@openismus.com>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-phone-utils.h"
+
+/* GLib headers */
+#include <glib/gi18n-lib.h>
+
+#ifdef ENABLE_PHONENUMBER
+
+/* C++ standard library */
+#include <string>
+
+/* system headers */
+#include <langinfo.h>
+
+/* libphonenumber */
+#include <phonenumbers/logger.h>
+#include <phonenumbers/phonenumberutil.h>
+
+using i18n::phonenumbers::PhoneNumberUtil;
+
+#endif /* ENABLE_PHONENUMBER */
+
+struct _EPhoneNumber {
+#ifdef ENABLE_PHONENUMBER
+	i18n::phonenumbers::PhoneNumber phone_number;
+#endif /* ENABLE_PHONENUMBER */
+};
+
+G_DEFINE_BOXED_TYPE (EPhoneNumber,
+                     e_phone_number,
+                     e_phone_number_copy,
+                     e_phone_number_free)
+
+GQuark
+e_phone_number_error_quark (void)
+{
+	static GQuark q = 0;
+
+	if (q == 0)
+		q = g_quark_from_static_string ("e-phone-number-error-quark");
+
+	return q;
+}
+
+#ifdef ENABLE_PHONENUMBER
+
+static PhoneNumberUtil *
+e_phone_number_util_get_instance (void)
+{
+	static PhoneNumberUtil *instance = NULL;
+
+	if (g_once_init_enter (&instance)) {
+		/* FIXME: Ideally PhoneNumberUtil would not be a singleton,
+		 * so that we could safely tweak it's attributes without
+		 * influencing other users of the library. */
+		PhoneNumberUtil *new_instance = PhoneNumberUtil::GetInstance ();
+
+		/* Disable all logging: libphonenumber is pretty verbose. */
+		new_instance->SetLogger (new i18n::phonenumbers::NullLogger);
+		g_once_init_leave (&instance, new_instance);
+	}
+
+	return instance;
+}
+
+static EPhoneNumberError
+e_phone_number_error_code (PhoneNumberUtil::ErrorType error)
+{
+	switch (error) {
+	case PhoneNumberUtil::NO_PARSING_ERROR:
+		g_return_val_if_reached (E_PHONE_NUMBER_ERROR_UNKNOWN);
+	case PhoneNumberUtil::INVALID_COUNTRY_CODE_ERROR:
+		return E_PHONE_NUMBER_ERROR_INVALID_COUNTRY_CODE;
+	case PhoneNumberUtil::NOT_A_NUMBER:
+		return E_PHONE_NUMBER_ERROR_NOT_A_NUMBER;
+	case PhoneNumberUtil::TOO_SHORT_AFTER_IDD:
+		return E_PHONE_NUMBER_ERROR_TOO_SHORT_AFTER_IDD;
+	case PhoneNumberUtil::TOO_SHORT_NSN:
+		return E_PHONE_NUMBER_ERROR_TOO_SHORT;
+	case PhoneNumberUtil::TOO_LONG_NSN:
+		return E_PHONE_NUMBER_ERROR_TOO_LONG;
+	}
+
+	/* Please file a bug that we can add a proper error code. */
+	g_return_val_if_reached (E_PHONE_NUMBER_ERROR_UNKNOWN);
+}
+
+#endif /* ENABLE_PHONENUMBER */
+
+static const gchar *
+e_phone_number_error_to_string (EPhoneNumberError code)
+{
+	switch (code) {
+	case E_PHONE_NUMBER_ERROR_NOT_IMPLEMENTED:
+		return _("The library was built without phone number support.");
+	case E_PHONE_NUMBER_ERROR_UNKNOWN:
+		return _("The phone number parser reported an yet unkown error code.");
+	case E_PHONE_NUMBER_ERROR_NOT_A_NUMBER:
+		return _("Not a phone number");
+	case E_PHONE_NUMBER_ERROR_INVALID_COUNTRY_CODE:
+		return _("Invalid country code");
+	case E_PHONE_NUMBER_ERROR_TOO_SHORT_AFTER_IDD:
+		return _("Remaining text after the country code is to short for a phone number");
+	case E_PHONE_NUMBER_ERROR_TOO_SHORT:
+		return _("Text is too short for a phone number");
+	case E_PHONE_NUMBER_ERROR_TOO_LONG:
+		return _("Text is too long for a phone number");
+	}
+
+	return _("Unknown error");
+}
+
+static void
+e_phone_number_set_error (GError **error,
+                          EPhoneNumberError code)
+{
+	const gchar *message = e_phone_number_error_to_string (code);
+	g_set_error_literal (error, E_PHONE_NUMBER_ERROR, code, message);
+}
+
+gboolean
+e_phone_number_is_supported (void)
+{
+#ifdef ENABLE_PHONENUMBER
+	return TRUE;
+#else /* ENABLE_PHONENUMBER */
+	return FALSE;
+#endif /* ENABLE_PHONENUMBER */
+}
+
+EPhoneNumber *
+e_phone_number_from_string (const gchar *phone_number,
+                            const gchar *country_code,
+                            GError **error)
+{
+	EPhoneNumber *parsed_number = NULL;
+
+	g_return_val_if_fail (NULL != phone_number, NULL);
+
+#ifdef ENABLE_PHONENUMBER
+
+	if (country_code == NULL) {
+#if HAVE__NL_ADDRESS_COUNTRY_AB2
+		country_code = nl_langinfo (_NL_ADDRESS_COUNTRY_AB2);
+#else /* HAVE__NL_ADDRESS_COUNTRY_AB2 */
+#error Cannot resolve default 2-letter country code. Find a replacement for _NL_ADDRESS_COUNTRY_AB2 or implement code to parse the locale name.
+#endif /* HAVE__NL_ADDRESS_COUNTRY_AB2 */
+	}
+
+	parsed_number = new EPhoneNumber;
+
+	const PhoneNumberUtil::ErrorType err =
+		e_phone_number_util_get_instance ()->Parse (phone_number, country_code,
+		                                            &parsed_number->phone_number);
+
+	if (err != PhoneNumberUtil::NO_PARSING_ERROR) {
+		e_phone_number_set_error (error, e_phone_number_error_code (err));
+
+		delete parsed_number;
+		parsed_number = NULL;
+	}
+
+#else /* ENABLE_PHONENUMBER */
+
+	e_phone_number_set_error (error, E_PHONE_NUMBER_ERROR_NOT_IMPLEMENTED);
+
+#endif /* ENABLE_PHONENUMBER */
+
+	return parsed_number;
+}
+
+gchar *
+e_phone_number_to_string (const EPhoneNumber *phone_number,
+                          EPhoneNumberFormat format)
+{
+	g_return_val_if_fail (NULL != phone_number, NULL);
+
+#ifdef ENABLE_PHONENUMBER
+
+	std::string formatted_number;
+
+	e_phone_number_util_get_instance ()->Format
+		(phone_number->phone_number,
+		 static_cast<PhoneNumberUtil::PhoneNumberFormat> (format),
+		 &formatted_number);
+
+	if (!formatted_number.empty ())
+		return g_strdup (formatted_number.c_str ());
+
+#else /* ENABLE_PHONENUMBER */
+
+	g_warning ("%s: The library was built without phone number support.", G_STRFUNC);
+
+#endif /* ENABLE_PHONENUMBER */
+
+	return NULL;
+}
+
+static EPhoneNumberMatch
+e_phone_number_match (PhoneNumberUtil::MatchType match_type)
+{
+	switch (match_type) {
+	case PhoneNumberUtil::NO_MATCH:
+	case PhoneNumberUtil::INVALID_NUMBER:
+		return E_PHONE_NUMBER_MATCH_NONE;
+	case PhoneNumberUtil::SHORT_NSN_MATCH:
+		return E_PHONE_NUMBER_MATCH_SHORT;
+	case PhoneNumberUtil::NSN_MATCH:
+		return E_PHONE_NUMBER_MATCH_NATIONAL;
+	case PhoneNumberUtil::EXACT_MATCH:
+		return E_PHONE_NUMBER_MATCH_EXACT;
+	}
+
+	g_return_val_if_reached (E_PHONE_NUMBER_MATCH_NONE);
+}
+
+EPhoneNumberMatch
+e_phone_number_compare	(const EPhoneNumber *first_number,
+			 const EPhoneNumber *second_number)
+{
+	g_return_val_if_fail (NULL != first_number, E_PHONE_NUMBER_MATCH_NONE);
+	g_return_val_if_fail (NULL != second_number, E_PHONE_NUMBER_MATCH_NONE);
+
+	EPhoneNumberMatch result = E_PHONE_NUMBER_MATCH_NONE;
+
+#ifdef ENABLE_PHONENUMBER
+
+	const PhoneNumberUtil::MatchType match_type =
+		e_phone_number_util_get_instance ()->
+		IsNumberMatch (first_number->phone_number,
+		               second_number->phone_number);
+
+	g_warn_if_fail (match_type != PhoneNumberUtil::INVALID_NUMBER);
+	result = e_phone_number_match (match_type);
+
+#else /* ENABLE_PHONENUMBER */
+
+	e_phone_number_set_error (error, E_PHONE_NUMBER_ERROR_NOT_IMPLEMENTED);
+
+#endif /* ENABLE_PHONENUMBER */
+
+	return result;
+}
+
+EPhoneNumberMatch
+e_phone_number_compare_strings	(const gchar *first_number,
+				 const gchar *second_number,
+				 GError **error)
+{
+	g_return_val_if_fail (NULL != first_number, E_PHONE_NUMBER_MATCH_NONE);
+	g_return_val_if_fail (NULL != second_number, E_PHONE_NUMBER_MATCH_NONE);
+
+	EPhoneNumberMatch result = E_PHONE_NUMBER_MATCH_NONE;
+
+#ifdef ENABLE_PHONENUMBER
+
+	const PhoneNumberUtil::MatchType match_type =
+		e_phone_number_util_get_instance ()->
+		IsNumberMatchWithTwoStrings (first_number, second_number);
+
+	if (match_type == PhoneNumberUtil::INVALID_NUMBER) {
+		e_phone_number_set_error (error, E_PHONE_NUMBER_ERROR_NOT_A_NUMBER);
+	} else {
+		result = e_phone_number_match (match_type);
+	}
+
+#else /* ENABLE_PHONENUMBER */
+
+	e_phone_number_set_error (error, E_PHONE_NUMBER_ERROR_NOT_IMPLEMENTED);
+
+#endif /* ENABLE_PHONENUMBER */
+
+	return result;
+}
+
+EPhoneNumber *
+e_phone_number_copy (const EPhoneNumber *phone_number)
+{
+	if (phone_number)
+		return new EPhoneNumber (*phone_number);
+
+	return NULL;
+}
+
+void
+e_phone_number_free (EPhoneNumber *phone_number)
+{
+	delete phone_number;
+}
diff --git a/libedataserver/e-phone-utils.h b/libedataserver/e-phone-utils.h
new file mode 100644
index 0000000..1ee9f39
--- /dev/null
+++ b/libedataserver/e-phone-utils.h
@@ -0,0 +1,224 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright Copyright (C) 2012 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser 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 Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * Author: Mathias Hasselmann <mathias@openismus.com>
+ */
+
+/* NOTE: Keeping API documentation in this header file because gtkdoc-mkdb
+ * explicitly only scans .h and .c files, but ignores .cpp files. */
+
+/**
+ * SECTION: e-phone-utils
+ * @include: libedataserver/libedataserver.h
+ * @short_description: Phone number support
+ *
+ * This modules provides utility functions for parsing and formatting
+ * phone numbers. Under the hood it uses Google's libphonenumber.
+ **/
+
+#if !defined (__LIBEDATASERVER_H_INSIDE__) && !defined (LIBEDATASERVER_COMPILATION)
+#error "Only <libedataserver/libedataserver.h> should be included directly."
+#endif
+
+#ifndef E_PHONE_UTILS_H
+#define E_PHONE_UTILS_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define E_TYPE_PHONE_NUMBER (e_phone_number_get_type ())
+#define E_PHONE_NUMBER_ERROR (e_phone_number_error_quark ())
+
+/**
+ * EPhoneNumberFormat:
+ * @E_PHONE_NUMBER_FORMAT_E164: format according E.164: "+493055667788".
+ * @E_PHONE_NUMBER_FORMAT_INTERNATIONAL: a formatted phone number always
+ * starting with the country calling code: "+49 30 55667788".
+ * @E_PHONE_NUMBER_FORMAT_NATIONAL: a formatted phone number in national
+ * scope, that is without country code: "(030) 55667788".
+ * @E_PHONE_NUMBER_FORMAT_RFC3966: a tel: URL according to RFC 3966:
+ * "tel:+49-30-55667788".
+ *
+ * The supported formatting rules for phone numbers.
+ **/
+typedef enum {
+	E_PHONE_NUMBER_FORMAT_E164,
+	E_PHONE_NUMBER_FORMAT_INTERNATIONAL,
+	E_PHONE_NUMBER_FORMAT_NATIONAL,
+	E_PHONE_NUMBER_FORMAT_RFC3966
+} EPhoneNumberFormat;
+
+/**
+ * EPhoneNumberMatch:
+ * @E_PHONE_NUMBER_MATCH_NONE: The phone numbers did not match.
+ * @E_PHONE_NUMBER_MATCH_EXACT: The phone numbers matched exactly.
+ * @E_PHONE_NUMBER_MATCH_NATIONAL: There was no country code for at least
+ * one of the numbers, but the national parts matched.
+ * @E_PHONE_NUMBER_MATCH_SHORT: There was no country code for at least
+ * one of the numbers, but one number might be part (suffix) of the other.
+ */
+typedef enum {
+	E_PHONE_NUMBER_MATCH_NONE,
+	E_PHONE_NUMBER_MATCH_EXACT,
+	E_PHONE_NUMBER_MATCH_NATIONAL = 1024,
+	E_PHONE_NUMBER_MATCH_SHORT = 2048
+} EPhoneNumberMatch;
+
+/**
+ * EPhoneNumberError:
+ * @E_PHONE_NUMBER_ERROR_NOT_IMPLEMENTED: the library was built without phone
+ * number support
+ * @E_PHONE_NUMBER_ERROR_UNKNOWN: the phone number parser reported an yet
+ * unkown error code.
+ * @E_PHONE_NUMBER_ERROR_INVALID_COUNTRY_CODE: the supplied phone number has an
+ * invalid country code.
+ * @E_PHONE_NUMBER_ERROR_NOT_A_NUMBER: the supplied text is not a phone number.
+ * @E_PHONE_NUMBER_ERROR_TOO_SHORT_AFTER_IDD: the remaining text after the
+ * country code is to short for a phone number.
+ * @E_PHONE_NUMBER_ERROR_TOO_SHORT: the text is too short for a phone number.
+ * @E_PHONE_NUMBER_ERROR_TOO_LONG: the text is too long for a phone number.
+ *
+ * Numeric description of a phone number related error.
+ **/
+typedef enum {
+	E_PHONE_NUMBER_ERROR_NOT_IMPLEMENTED,
+	E_PHONE_NUMBER_ERROR_UNKNOWN,
+	E_PHONE_NUMBER_ERROR_NOT_A_NUMBER,
+	E_PHONE_NUMBER_ERROR_INVALID_COUNTRY_CODE,
+	E_PHONE_NUMBER_ERROR_TOO_SHORT_AFTER_IDD,
+	E_PHONE_NUMBER_ERROR_TOO_SHORT,
+	E_PHONE_NUMBER_ERROR_TOO_LONG,
+} EPhoneNumberError;
+
+/**
+ * EPhoneNumber:
+ * This opaque type describes a parsed phone number. It can be copied using
+ * e_phone_number_copy(). To release it call e_phone_number_free().
+ */
+typedef struct _EPhoneNumber EPhoneNumber;
+
+GType			e_phone_number_get_type		(void) G_GNUC_CONST;
+GQuark			e_phone_number_error_quark	(void) G_GNUC_CONST;
+
+/**
+ * e_phone_number_is_supported:
+ *
+ * Checks if phone number support is available. It is recommended to call this
+ * function before using any of the phone-utils functions to ensure that the
+ * required functionality is available, and to pick alternative mechnisms if
+ * needed.
+ *
+ * Returns: %TRUE if phone number support is available.
+ */
+gboolean		e_phone_number_is_supported	(void) G_GNUC_CONST;
+
+/**
+ * e_phone_number_from_string:
+ * @phone_number: the phone number to parse
+ * @country_code: (allow-none): a 2-letter country code, or %NULL
+ * @error: (out): a #GError to set an error, if any
+ *
+ * Parses the string passed in @phone_number. Note that no validation is
+ * performed whether the recognized phone number is valid for a particular
+ * region.
+ *
+ * The 2-letter country code passed in @country_code only is used if the
+ * @phone_number is not written in international format. If the number is
+ * guaranteed to start with a '+' followed by the country calling code,
+ * then "ZZ" can be passed here.
+ *
+ * Returns: (transfer full): a new EPhoneNumber instance on success,
+ * or %NULL on error. Call e_phone_number_free() to release this instance.
+ *
+ * Since: 3.8
+ **/
+EPhoneNumber *		e_phone_number_from_string	(const gchar *phone_number,
+							 const gchar *country_code,
+							 GError **error);
+
+/**
+ * e_phone_number_to_string:
+ * @phone_number: the phone number to format
+ * @format: the phone number format to apply
+ *
+ * Describes the @phone_number according to the rules applying to @format.
+ *
+ * Returns: (transfer full): A formatted string for @phone_number.
+ *
+ * Since: 3.8
+ **/
+gchar *			e_phone_number_to_string	(const EPhoneNumber *phone_number,
+							 EPhoneNumberFormat format);
+
+/**
+ * e_phone_number_compare:
+ * @first_number: the first EPhoneNumber to compare
+ * @second_number: the second EPhoneNumber to compare
+ *
+ * Compares two phone numbers.
+ *
+ * Returns: The quality of matching for the two phone numbers.
+ *
+ * Since: 3.8
+ */
+EPhoneNumberMatch	e_phone_number_compare		(const EPhoneNumber *first_number,
+							 const EPhoneNumber *second_number);
+
+/**
+ * e_phone_number_compare_strings:
+ * @first_number: the first EPhoneNumber to compare
+ * @second_number: the second EPhoneNumber to compare
+ * @error: (out): a #GError to set an error, if any
+ *
+ * Compares two phone numbers.
+ *
+ * Returns: The quality of matching for the two phone numbers.
+ *
+ * Since: 3.8
+ */
+EPhoneNumberMatch	e_phone_number_compare_strings	(const gchar *first_number,
+							 const gchar *second_number,
+							 GError **error);
+
+/**
+ * e_phone_number_copy:
+ * @phone_number: the EPhoneNumber to copy
+ *
+ * Makes a copy of @phone_number.
+ *
+ * Returns: (transfer full): A newly allocated EPhoneNumber instance.
+ * Call e_phone_number_free() to release this instance.
+ *
+ * Since: 3.8
+ **/
+EPhoneNumber *		e_phone_number_copy		(const EPhoneNumber *phone_number);
+
+/**
+ * e_phone_number_free:
+ * @phone_number: the EPhoneNumber to free
+ *
+ * Released the memory occupied by @phone_number.
+ *
+ * Since: 3.8
+ **/
+void			e_phone_number_free		(EPhoneNumber *phone_number);
+
+G_END_DECLS
+
+#endif /* E_BOOK_BACKEND_FILE_PHONE_UTILS_H */
diff --git a/libedataserver/e-sexp.c b/libedataserver/e-sexp.c
index 0e057a9..67ad75b 100644
--- a/libedataserver/e-sexp.c
+++ b/libedataserver/e-sexp.c
@@ -1672,10 +1672,8 @@ e_sexp_parse (ESExp *f)
 {
 	g_return_val_if_fail (IS_E_SEXP (f), -1);
 
-	if (setjmp (f->failenv)) {
-		g_warning ("Error in parsing: %s", f->error);
+	if (setjmp (f->failenv))
 		return -1;
-	}
 
 	if (f->tree)
 		parse_term_free (f, f->tree);
diff --git a/libedataserver/libedataserver.h b/libedataserver/libedataserver.h
index ca98003..8ee2993 100644
--- a/libedataserver/libedataserver.h
+++ b/libedataserver/libedataserver.h
@@ -34,6 +34,7 @@
 #include <libedataserver/e-list.h>
 #include <libedataserver/e-memory.h>
 #include <libedataserver/e-operation-pool.h>
+#include <libedataserver/e-phone-utils.h>
 #include <libedataserver/e-proxy.h>
 #include <libedataserver/e-sexp.h>
 #include <libedataserver/e-source-address-book.h>
diff --git a/m4/evo_check_langinfo.m4 b/m4/evo_check_langinfo.m4
new file mode 100644
index 0000000..a59ab29
--- /dev/null
+++ b/m4/evo_check_langinfo.m4
@@ -0,0 +1,25 @@
+dnl EVO_CHECK_LANGINFO(detail)
+dnl Checks if the given langinfo detail is supported
+AC_DEFUN([EVO_CHECK_LANGINFO],[
+	AS_VAR_PUSHDEF([ac_cv_langinfo_detail],
+	               [ac_cv_langinfo_]m4_tolower($1))
+
+	AC_MSG_CHECKING([for nl_langinfo ($1)])
+
+	AC_LANG_PUSH(C)
+
+	AC_COMPILE_IFELSE(
+		[AC_LANG_PROGRAM(
+			[[#include <langinfo.h>]],
+			[[char *detail = nl_langinfo ($1);]])],
+		[ac_cv_langinfo_detail=yes],
+		[ac_cv_langinfo_detail=no])
+
+	AC_LANG_POP(C)
+
+	AS_VAR_IF([ac_cv_langinfo_detail], [yes],
+	          [AC_DEFINE([HAVE_]m4_toupper($1), 1, [Have nl_langinfo ($1)])])
+
+	AC_MSG_RESULT([$ac_cv_langinfo_detail])
+	AS_VAR_POPDEF([ac_cv_langinfo_detail])
+])
diff --git a/m4/evo_phonenumber.m4 b/m4/evo_phonenumber.m4
new file mode 100644
index 0000000..395a3bc
--- /dev/null
+++ b/m4/evo_phonenumber.m4
@@ -0,0 +1,68 @@
+dnl EVO_PHONENUMBER_SUPPORT([default])
+dnl Check for Google's libphonenumber. Adds a --with-phonenumber option
+dnl to explicitly enable and disable phonenumber support, but also for
+dnl pointing to libphonenumber's install prefix.
+AC_DEFUN([EVO_PHONENUMBER_SUPPORT],[
+	AC_MSG_CHECKING([whether to enable phonenumber support])
+
+	evo_phonenumber_prefix=
+	msg_phonenumber=no
+
+	PHONENUMBER_INCLUDES=
+	PHONENUMBER_LIBS=
+
+	AC_ARG_WITH([phonenumber],
+		[AS_HELP_STRING([--with-phonenumber@<:@=PREFIX@:>@],
+		                [use libphonenumber at PREFIX])],
+		[evo_phonenumber_prefix=$withval],
+		[with_phonenumber=m4_default([$1],[check])])
+
+	AC_MSG_RESULT([$with_phonenumber])
+
+	AS_VAR_IF([with_phonenumber], [no],, [
+		AC_LANG_PUSH(C++)
+
+		PHONENUMBER_LIBS="-lphonenumber -lboost_thread"
+
+		AS_VAR_IF([evo_phonenumber_prefix],,,[
+			PHONENUMBER_INCLUDES="-I$evo_phonenumber_prefix/include"
+			PHONENUMBER_LIBS="-L$evo_phonenumber_prefix/lib $PHONENUMBER_LIBS"
+		])
+
+		evo_cxxflags_saved="$CXXFLAGS"
+		CXXFLAGS="$CXXFLAGS $PHONENUMBER_INCLUDES"
+
+		evo_libs_saved="$LIBS"
+		LIBS="$LIBS $PHONENUMBER_LIBS"
+
+		AC_MSG_CHECKING([if libphonenumber is usable])
+		AC_LINK_IFELSE(
+			[AC_LANG_PROGRAM(
+				[[#include <phonenumbers/phonenumberutil.h>]],
+				[[i18n::phonenumbers::PhoneNumberUtil::GetInstance();]])],
+			[with_phonenumber=yes],
+			[AS_VAR_IF([with_phonenumber], [check], [with_phonenumber=no], [
+				AC_MSG_ERROR([libphonenumber cannot be used. Use --with-phonenumber to specify the library prefix.])])
+			])
+
+		CXXFLAGS="$evo_cxxflags_saved"
+		LDFLAGS="$evo_ldflags_saved"
+		LIBS="$evo_libs_saved"
+
+		AS_VAR_IF([evo_phonenumber_prefix],,
+		          [msg_phonenumber=$with_phonenumber],
+		          [msg_phonenumber=$evo_phonenumber_prefix])
+
+		AC_MSG_RESULT([$with_phonenumber])
+		AC_LANG_POP(C++)
+	])
+
+	AM_CONDITIONAL([ENABLE_PHONENUMBER],
+	               [test "x$with_phonenumber" != "xno"])
+
+	AS_VAR_IF([with_phonenumber], [yes],
+	          [AC_DEFINE([ENABLE_PHONENUMBER], 1, [Enable phonenumber parsing])])
+
+	AC_SUBST([PHONENUMBER_INCLUDES])
+	AC_SUBST([PHONENUMBER_LIBS])
+])
diff --git a/tests/libebook/client/Makefile.am b/tests/libebook/client/Makefile.am
index 83900de..3972b86 100644
--- a/tests/libebook/client/Makefile.am
+++ b/tests/libebook/client/Makefile.am
@@ -1,6 +1,11 @@
 noinst_LTLIBRARIES = libclient-test-utils.la
 
-libclient_test_utils_la_SOURCES = client-test-utils.c client-test-utils.h
+libclient_test_utils_la_SOURCES =				\
+	client-test-utils.c					\
+	client-test-utils.h					\
+	$(top_srcdir)/tests/libedataserver/e-test-dbus-utils.c	\
+	$(top_srcdir)/tests/libedataserver/e-test-dbus-utils.h	\
+	$(NULL)
 
 libclient_test_utils_la_CPPFLAGS =				\
 	$(AM_CPPFLAGS)						\
@@ -8,7 +13,9 @@ libclient_test_utils_la_CPPFLAGS =				\
 	-I$(top_builddir)					\
 	-I$(top_srcdir)/addressbook				\
 	-I$(top_builddir)/addressbook				\
-	-DBACKENDDIR=\"$(ebook_backenddir)\" 			\
+	-I$(top_srcdir)/tests/libedataserver			\
+	-DBACKENDDIR=\"$(ebook_backenddir)\"			\
+	-DDATADIR=\"$(datadir)\"				\
 	-DSRCDIR=\""$(abs_srcdir)"\"				\
 	$(EVOLUTION_ADDRESSBOOK_CFLAGS)				\
 	$(CAMEL_CFLAGS)						\
diff --git a/tests/libebook/client/client-test-utils.c b/tests/libebook/client/client-test-utils.c
index 578d851..5ea2038 100644
--- a/tests/libebook/client/client-test-utils.c
+++ b/tests/libebook/client/client-test-utils.c
@@ -6,6 +6,7 @@
 #include <libedataserver/libedataserver.h>
 
 #include "client-test-utils.h"
+#include "e-test-dbus-utils.h"
 
 /* This forces the GType to be registered in a way that
  * avoids a "statement with no effect" compiler warning.
@@ -76,6 +77,7 @@ void
 main_initialize (void)
 {
 	static gboolean initialized = FALSE;
+	GTestDBus *test_dbus = NULL;
 
 	if (initialized)
 		return;
@@ -83,6 +85,14 @@ main_initialize (void)
 	g_type_init ();
 	e_gdbus_templates_init_main_thread ();
 
+	e_test_setup_base_directories ();
+	test_dbus = e_test_setup_dbus_session ();
+
+	g_test_dbus_up (test_dbus);
+
+	g_print ("Using private D-Bus session for testing: \"%s\"\n",
+                 g_test_dbus_get_bus_address (test_dbus));
+
 	initialized = TRUE;
 }
 
diff --git a/tests/libebook/data/vcards/custom-1.vcf b/tests/libebook/data/vcards/custom-1.vcf
index bc9a23c..6d7241b 100644
--- a/tests/libebook/data/vcards/custom-1.vcf
+++ b/tests/libebook/data/vcards/custom-1.vcf
@@ -1,5 +1,5 @@
 BEGIN:VCARD
 FN:Micheal Jackson
-TEL;HOME:+1234567
+TEL;HOME:+1-221-5423789
 EMAIL;TYPE=home,work:micheal@jackson.com
 END:VCARD
diff --git a/tests/libebook/test-query.c b/tests/libebook/test-query.c
index 791383c..f2a155b 100644
--- a/tests/libebook/test-query.c
+++ b/tests/libebook/test-query.c
@@ -4,35 +4,156 @@
 #define QUERY_STRING1
 #define QUERY_STRING2
 
-static const gchar * queries[] = {
-	"(exists \"full_name\")",
-	"(contains \"full_name\" \"Miguel\")"
+typedef struct {
+	EBookQuery *query;
+	char *sexp;
+} TestData;
 
-	/* XXX come on, add more here */
-};
+static void
+normalize_space (char *str)
+{
+	while (*str) {
+		char *tail;
+
+		if (*str++ != ' ')
+			continue;
+
+		tail = str;
+
+		while (*tail == ' ')
+			++tail;
+
+		memmove (str, tail, strlen (tail) + 1);
+	}
+}
+
+static void
+test_query (gconstpointer data)
+{
+	const TestData *const test = data;
+	EBookQuery *query;
+	char *sexp;
+
+	sexp = e_book_query_to_string (test->query);
+	normalize_space (sexp);
+
+	g_assert_cmpstr (test->sexp, ==, sexp);
+	g_free (sexp);
+
+	query = e_book_query_from_string (test->sexp);
+	sexp = e_book_query_to_string (query);
+	e_book_query_unref (query);
+	normalize_space (sexp);
+
+	g_assert_cmpstr (test->sexp, ==, sexp);
+	g_free (sexp);
+}
+
+static void
+test_data_free (gpointer data)
+{
+	TestData *const test = data;
+
+	e_book_query_unref (test->query);
+	g_free (test->sexp);
+
+	g_slice_free (TestData, test);
+}
+
+static void
+add_query_test (const char *path,
+                EBookQuery *query,
+                const char *sexp)
+{
+	TestData *data = g_slice_new (TestData);
+
+	data->sexp = g_strdup (sexp);
+	data->query = query;
+
+	g_test_add_data_func_full (path, data, test_query, test_data_free);
+}
 
 gint
 main (gint argc,
       gchar **argv)
 {
-	gint i;
-	gboolean failure = FALSE;
+	g_type_init ();
 
-	for (i = 0; i < G_N_ELEMENTS (queries); i++) {
-		EBookQuery *query = e_book_query_from_string (queries[i]);
-		gchar *str;
+	g_test_init (&argc, &argv, NULL);
 
-		str = e_book_query_to_string (query);
+	add_query_test ("/libebook/test-query/sexp/all",
+	                e_book_query_any_field_contains (NULL),
+	                "(contains \"x-evolution-any-field\" \"\")");
 
-		if (strcmp (str, queries[i])) {
-			g_warning ("failed on query: %s", queries[i]);
+	add_query_test ("/libebook/test-query/sexp/any",
+	                e_book_query_any_field_contains ("liberty"),
+	                "(contains \"x-evolution-any-field\" \"liberty\")");
 
-			failure = TRUE;
-		}
-	}
+	add_query_test ("/libebook/test-query/sexp/not",
+	                e_book_query_not (e_book_query_any_field_contains ("liberty"), TRUE),
+	                "(not (contains \"x-evolution-any-field\" \"liberty\"))");
+
+	add_query_test ("/libebook/test-query/sexp/and",
+	                e_book_query_andv (e_book_query_any_field_contains ("liberty"),
+	                                   e_book_query_any_field_contains ("friendship"),
+	                                   NULL),
+	                "(and (contains \"x-evolution-any-field\" \"liberty\")"
+	                    " (contains \"x-evolution-any-field\" \"friendship\")"
+	                    " )");
+
+	add_query_test ("/libebook/test-query/sexp/or",
+	                e_book_query_orv (e_book_query_any_field_contains ("liberty"),
+	                                  e_book_query_any_field_contains ("friendship"),
+	                                  NULL),
+	                "(or (contains \"x-evolution-any-field\" \"liberty\")"
+	                   " (contains \"x-evolution-any-field\" \"friendship\")"
+	                   " )");
+
+	add_query_test ("/libebook/test-query/sexp/exists",
+	                e_book_query_field_exists (E_CONTACT_FULL_NAME),
+	                "(exists \"full_name\")");
+
+	add_query_test ("/libebook/test-query/sexp/contains",
+	                e_book_query_field_test (E_CONTACT_FULL_NAME,
+	                                         E_BOOK_QUERY_CONTAINS,
+	                                         "Miguel"),
+	                "(contains \"full_name\" \"Miguel\")");
+
+	add_query_test ("/libebook/test-query/sexp/is",
+	                e_book_query_field_test (E_CONTACT_GIVEN_NAME,
+	                                         E_BOOK_QUERY_IS,
+	                                         "Miguel"),
+	                "(is \"given_name\" \"Miguel\")");
+
+	add_query_test ("/libebook/test-query/sexp/beginswith",
+	                e_book_query_field_test (E_CONTACT_FULL_NAME,
+	                                         E_BOOK_QUERY_BEGINS_WITH,
+	                                         "Mig"),
+	                "(beginswith \"full_name\" \"Mig\")");
+
+	add_query_test ("/libebook/test-query/sexp/endswith",
+	                e_book_query_field_test (E_CONTACT_TEL,
+	                                         E_BOOK_QUERY_ENDS_WITH,
+	                                         "5423789"),
+	                "(endswith \"phone\" \"5423789\")");
+
+	add_query_test ("/libebook/test-query/sexp/eqphone",
+
+	                e_book_query_orv (e_book_query_field_test (E_CONTACT_TEL,
+	                                                           E_BOOK_QUERY_EQUALS_PHONE_NUMBER,
+	                                                           "+1-2215423789"),
+	                                  e_book_query_field_test (E_CONTACT_TEL,
+	                                                           E_BOOK_QUERY_EQUALS_NATIONAL_PHONE_NUMBER,
+	                                                           "2215423789"),
+	                                  e_book_query_field_test (E_CONTACT_TEL,
+	                                                           E_BOOK_QUERY_EQUALS_SHORT_PHONE_NUMBER,
+	                                                           "5423789"),
+	                                  NULL),
 
-	if (!failure)
-	  g_message ("all tests passed");
+	                "(or (eqphone \"phone\" \"+1-2215423789\")"
+	                   " (eqphone_national \"phone\" \"2215423789\")"
+	                   " (eqphone_short \"phone\" \"5423789\")"
+	                   " )");
 
-	return 0;
+	return g_test_run ();
 }
diff --git a/tests/libedataserver/Makefile.am b/tests/libedataserver/Makefile.am
index 2019aa8..e5f1927 100644
--- a/tests/libedataserver/Makefile.am
+++ b/tests/libedataserver/Makefile.am
@@ -1,17 +1,26 @@
-TESTS = e-source-test
+TESTS = \
+	e-phone-utils-test \
+	e-source-test \
+	$(NULL)
 
 noinst_PROGRAMS = $(TESTS)
 
-e_source_test_CPPFLAGS =					\
+TEST_CPPFLAGS =							\
 	$(AM_CPPFLAGS)						\
 	-I$(top_srcdir)						\
 	-DG_LOG_DOMAIN=\"e-data-server\"			\
 	$(E_DATA_SERVER_CFLAGS)					\
 	$(GIO_UNIX_CFLAGS)
 
-e_source_test_LDADD =						\
+TEST_LDADD =							\
 	$(top_builddir)/libedataserver/libedataserver-1.2.la	\
 	$(E_DATA_SERVER_LIBS)					\
 	$(GIO_UNIX_LIBS)
 
+e_phone_utils_test_CPPFLAGS = $(TEST_CPPFLAGS)
+e_phone_utils_test_LDADD = $(TEST_LDADD)
+
+e_source_test_CPPFLAGS = $(TEST_CPPFLAGS)
+e_source_test_LDADD = $(TEST_LDADD)
+
 -include $(top_srcdir)/git.mk
diff --git a/tests/libedataserver/e-phone-utils-test.c b/tests/libedataserver/e-phone-utils-test.c
new file mode 100644
index 0000000..32d9d17
--- /dev/null
+++ b/tests/libedataserver/e-phone-utils-test.c
@@ -0,0 +1,250 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright Copyright (C) 2012 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser 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 Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * Author: Mathias Hasselmann <mathias@openismus.com>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <libedataserver/libedataserver.h>
+
+static const char *match_candidates[] = {
+	"not-a-number",
+	"+1-617-4663489", "617-4663489", "4663489",
+	"+1.408.845.5246", "4088455246", "8455246"
+};
+
+static const EPhoneNumberMatch expected_matches[] = {
+	/* not a number */
+	E_PHONE_NUMBER_MATCH_NONE,
+	E_PHONE_NUMBER_MATCH_NONE,
+	E_PHONE_NUMBER_MATCH_NONE,
+	E_PHONE_NUMBER_MATCH_NONE,
+	E_PHONE_NUMBER_MATCH_NONE,
+	E_PHONE_NUMBER_MATCH_NONE,
+	E_PHONE_NUMBER_MATCH_NONE,
+
+	/* +1-617-4663489 */
+	E_PHONE_NUMBER_MATCH_NONE,
+	E_PHONE_NUMBER_MATCH_EXACT,
+	E_PHONE_NUMBER_MATCH_NATIONAL,
+	E_PHONE_NUMBER_MATCH_SHORT,
+	E_PHONE_NUMBER_MATCH_NONE,
+	E_PHONE_NUMBER_MATCH_NONE,
+	E_PHONE_NUMBER_MATCH_NONE,
+
+	E_PHONE_NUMBER_MATCH_NONE,
+	E_PHONE_NUMBER_MATCH_NATIONAL,
+	E_PHONE_NUMBER_MATCH_NATIONAL,
+	E_PHONE_NUMBER_MATCH_SHORT,
+	E_PHONE_NUMBER_MATCH_NONE,
+	E_PHONE_NUMBER_MATCH_NONE,
+	E_PHONE_NUMBER_MATCH_NONE,
+
+	E_PHONE_NUMBER_MATCH_NONE,
+	E_PHONE_NUMBER_MATCH_SHORT,
+	E_PHONE_NUMBER_MATCH_SHORT,
+	E_PHONE_NUMBER_MATCH_NATIONAL, /* XXX - Google, sure? */
+	E_PHONE_NUMBER_MATCH_NONE,
+	E_PHONE_NUMBER_MATCH_NONE,
+	E_PHONE_NUMBER_MATCH_NONE,
+
+	/* +1.408.845.5246 */
+	E_PHONE_NUMBER_MATCH_NONE,
+	E_PHONE_NUMBER_MATCH_NONE,
+	E_PHONE_NUMBER_MATCH_NONE,
+	E_PHONE_NUMBER_MATCH_NONE,
+	E_PHONE_NUMBER_MATCH_EXACT,
+	E_PHONE_NUMBER_MATCH_NATIONAL,
+	E_PHONE_NUMBER_MATCH_SHORT,
+
+	E_PHONE_NUMBER_MATCH_NONE,
+	E_PHONE_NUMBER_MATCH_NONE,
+	E_PHONE_NUMBER_MATCH_NONE,
+	E_PHONE_NUMBER_MATCH_NONE,
+	E_PHONE_NUMBER_MATCH_NATIONAL,
+	E_PHONE_NUMBER_MATCH_NATIONAL,
+	E_PHONE_NUMBER_MATCH_SHORT,
+
+	E_PHONE_NUMBER_MATCH_NONE,
+	E_PHONE_NUMBER_MATCH_NONE,
+	E_PHONE_NUMBER_MATCH_NONE,
+	E_PHONE_NUMBER_MATCH_NONE,
+	E_PHONE_NUMBER_MATCH_SHORT,
+	E_PHONE_NUMBER_MATCH_SHORT,
+	E_PHONE_NUMBER_MATCH_NATIONAL /* XXX - Google, sure? */
+};
+
+static void
+test_parse_and_format (gconstpointer data)
+{
+	GError *error = NULL;
+	EPhoneNumber *parsed;
+	gchar **params;
+
+	params = g_strsplit (data, "/", G_MAXINT);
+	g_assert_cmpint (g_strv_length (params), ==, 6);
+
+	parsed = e_phone_number_from_string (params[0], params[1], &error);
+
+#ifdef ENABLE_PHONENUMBER
+
+	{
+		gchar **test_numbers;
+		gint i;
+
+		test_numbers = params + 2;
+
+		g_assert (parsed != NULL);
+		g_assert (error == NULL);
+
+		for (i = 0; test_numbers[i]; ++i) {
+			gchar *formatted;
+
+			formatted = e_phone_number_to_string (parsed, i);
+			g_assert (formatted != NULL);
+			g_assert_cmpstr (formatted, ==, test_numbers[i]);
+			g_free (formatted);
+		}
+
+		e_phone_number_free (parsed);
+	}
+
+#else /* ENABLE_PHONENUMBER */
+
+	g_assert (parsed == NULL);
+	g_assert (error != NULL);
+	g_assert (error->domain == E_PHONE_NUMBER_ERROR);
+	g_assert (error->code == E_PHONE_NUMBER_ERROR_NOT_IMPLEMENTED);
+	g_assert (error->message != NULL);
+
+#endif /* ENABLE_PHONENUMBER */
+
+	g_clear_error (&error);
+	g_strfreev (params);
+}
+
+static void
+test_parse_bad_number (void)
+{
+	GError *error = NULL;
+	EPhoneNumber *parsed;
+
+	parsed = e_phone_number_from_string ("+1-NOT-A-NUMBER", "US", &error);
+
+	g_assert (parsed == NULL);
+	g_assert (error != NULL);
+	g_assert (error->domain == E_PHONE_NUMBER_ERROR);
+#ifdef ENABLE_PHONENUMBER
+	g_assert (error->code == E_PHONE_NUMBER_ERROR_NOT_A_NUMBER);
+#else /* ENABLE_PHONENUMBER */
+	g_assert (error->code == E_PHONE_NUMBER_ERROR_NOT_IMPLEMENTED);
+#endif /* ENABLE_PHONENUMBER */
+	g_assert (error->message != NULL);
+
+	g_clear_error (&error);
+}
+
+static void
+test_compare_numbers (gconstpointer data)
+{
+	const size_t n = GPOINTER_TO_UINT (data);
+	const size_t i = n % G_N_ELEMENTS (match_candidates);
+	const size_t j = n / G_N_ELEMENTS (match_candidates);
+
+#ifdef ENABLE_PHONENUMBER
+	const gboolean error_expected = !(i && j) ;
+#else /* ENABLE_PHONENUMBER */
+	const gboolean error_expected = TRUE;
+#endif /* ENABLE_PHONENUMBER */
+
+	EPhoneNumberMatch actual_match;
+	GError *error = NULL;
+
+	actual_match = e_phone_number_compare_strings (match_candidates[i],
+	                                               match_candidates[j],
+	                                               &error);
+
+	g_assert_cmpuint (actual_match, ==, expected_matches[n]);
+
+	if (!error_expected) {
+		g_assert (error == NULL);
+	} else {
+		g_assert (error != NULL);
+		g_assert (error->domain == E_PHONE_NUMBER_ERROR);
+#ifdef ENABLE_PHONENUMBER
+		g_assert (error->code == E_PHONE_NUMBER_ERROR_NOT_A_NUMBER);
+#else /* ENABLE_PHONENUMBER */
+		g_assert (error->code == E_PHONE_NUMBER_ERROR_NOT_IMPLEMENTED);
+#endif /* ENABLE_PHONENUMBER */
+		g_assert (error->message != NULL);
+
+		g_clear_error (&error);
+	}
+}
+
+gint
+main (gint argc,
+      gchar **argv)
+{
+	size_t i, j;
+
+	g_type_init ();
+
+	g_test_init (&argc, &argv, NULL);
+
+	g_test_add_data_func
+		("/e-phone-utils-test/parse-and-format/i164",
+		 "+493011223344//+493011223344/+49 30 11223344/030 11223344/tel:+49-30-11223344",
+		 test_parse_and_format);
+	g_test_add_data_func
+		("/e-phone-utils-test/parse-and-format/national",
+		 "(030) 22334-455/DE/+493022334455/+49 30 22334455/030 22334455/tel:+49-30-22334455",
+		 test_parse_and_format);
+	g_test_add_data_func
+		("/e-phone-utils-test/parse-and-format/international",
+		 "+1 212 33445566//+121233445566/+1 21233445566/21233445566/tel:+1-21233445566",
+		 test_parse_and_format);
+	g_test_add_data_func
+		("/e-phone-utils-test/parse-and-format/rfc3966",
+		 "tel:+358-71-44556677//+3587144556677/+358 71 44556677/071 44556677/tel:+358-71-44556677",
+		 test_parse_and_format);
+
+	g_test_add_func
+		("/e-phone-utils-test/parse-and-format/BadNumber",
+		 test_parse_bad_number);
+
+	g_assert_cmpint (G_N_ELEMENTS (match_candidates) * G_N_ELEMENTS (match_candidates),
+			 ==, G_N_ELEMENTS (expected_matches));
+
+	for (i = 0; i < G_N_ELEMENTS (match_candidates); ++i) {
+		for (j = 0; j < G_N_ELEMENTS (match_candidates); ++j) {
+			const size_t n = j * G_N_ELEMENTS (match_candidates) + i;
+			char *path = g_strdup_printf ("/e-phone-utils-test/compare/%s/%s",
+			                              match_candidates[i],
+			                              match_candidates[j]);
+
+			g_test_add_data_func (path, GUINT_TO_POINTER (n), test_compare_numbers);
+			g_free (path);
+		}
+	}
+
+	return g_test_run ();
+}
diff --git a/tests/libedataserver/e-test-dbus-utils.c b/tests/libedataserver/e-test-dbus-utils.c
new file mode 100644
index 0000000..7bc592e
--- /dev/null
+++ b/tests/libedataserver/e-test-dbus-utils.c
@@ -0,0 +1,184 @@
+/*
+ * e-test-dbus-utils.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "e-test-dbus-utils.h"
+
+#include <libedataserver/libedataserver.h>
+
+static GOnce base_directories_once = G_ONCE_INIT;
+
+static void
+test_setup_xdg_data_home (const gchar *base_directory)
+{
+	gchar *filename;
+
+	g_assert (base_directory != NULL);
+
+	filename = g_build_filename (base_directory, "share", NULL);
+
+	g_print ("XDG_DATA_HOME=%s\n", filename);
+	g_assert (g_setenv ("XDG_DATA_HOME", filename, TRUE));
+	g_assert_cmpstr (g_get_user_data_dir (), ==, filename);
+
+	g_free (filename);
+}
+
+static void
+test_setup_xdg_cache_home (const gchar *base_directory)
+{
+	gchar *filename;
+
+	g_assert (base_directory != NULL);
+
+	filename = g_build_filename (base_directory, "cache", NULL);
+
+	g_print ("XDG_CACHE_HOME=%s\n", filename);
+	g_assert (g_setenv ("XDG_CACHE_HOME", filename, TRUE));
+	g_assert_cmpstr (g_get_user_cache_dir (), ==, filename);
+
+	g_free (filename);
+}
+
+static void
+test_setup_xdg_config_home (const gchar *base_directory)
+{
+	gchar *filename;
+
+	g_assert (base_directory != NULL);
+
+	filename = g_build_filename (base_directory, "config", NULL);
+
+	g_print ("XDG_CONFIG_HOME=%s\n", filename);
+	g_assert (g_setenv ("XDG_CONFIG_HOME", filename, TRUE));
+	g_assert_cmpstr (g_get_user_config_dir (), ==, filename);
+
+	g_free (filename);
+}
+
+static gpointer
+test_setup_base_directories_once (gpointer unused)
+{
+	gchar *base_directory;
+
+	/* Do not invoke g_get_user_cache_dir() here because that will
+	 * permanently set the cache directory result and we won't be
+	 * able to override it with an environment variable. */
+	base_directory = g_build_filename (
+		g_get_home_dir (),
+		".cache", "evolution",
+		"tmp", "test-home-XXXXXX", NULL);
+
+	mkdtemp (base_directory);
+
+	test_setup_xdg_data_home (base_directory);
+	test_setup_xdg_cache_home (base_directory);
+	test_setup_xdg_config_home (base_directory);
+
+	return base_directory;
+}
+
+const gchar *
+e_test_setup_base_directories (void)
+{
+	g_once (
+		&base_directories_once,
+		test_setup_base_directories_once, NULL);
+
+	return base_directories_once.retval;
+}
+
+gboolean
+e_test_clean_base_directories (GError **error)
+{
+	GFile *file;
+	gboolean success = TRUE;
+	const gchar *user_data_dir;
+	const gchar *user_cache_dir;
+	const gchar *user_config_dir;
+
+	e_test_setup_base_directories ();
+
+	user_data_dir = g_get_user_data_dir ();
+	g_assert (strstr (user_data_dir, "test-home") != NULL);
+
+	user_cache_dir = g_get_user_cache_dir ();
+	g_assert (strstr (user_cache_dir, "test-home") != NULL);
+
+	user_config_dir = g_get_user_config_dir ();
+	g_assert (strstr (user_config_dir, "test-home") != NULL);
+
+	if (success) {
+		file = g_file_new_for_path (user_data_dir);
+		success = e_file_recursive_delete_sync (file, NULL, error);
+		g_object_unref (file);
+	}
+
+	if (success) {
+		file = g_file_new_for_path (user_cache_dir);
+		success = e_file_recursive_delete_sync (file, NULL, error);
+		g_object_unref (file);
+	}
+
+	if (success) {
+		file = g_file_new_for_path (user_config_dir);
+		success = e_file_recursive_delete_sync (file, NULL, error);
+		g_object_unref (file);
+	}
+
+	return success;
+}
+
+static void
+register_dbus_service_dir (GTestDBus *test_dbus,
+                           const gchar *data_dir)
+{
+	gchar *service_dir;
+
+	service_dir = g_build_filename (data_dir, "dbus-1", "services", NULL);
+
+	g_print ("Adding service dir: %s\n", service_dir);
+	g_test_dbus_add_service_dir (test_dbus, service_dir);
+
+	g_free (service_dir);
+}
+
+GTestDBus *
+e_test_setup_dbus_session (void)
+{
+	GTestDBus *test_dbus;
+	const gchar * const *system_data_dirs;
+	gint ii;
+
+	e_test_setup_base_directories ();
+
+	system_data_dirs = g_get_system_data_dirs ();
+	g_assert (system_data_dirs != NULL);
+
+	test_dbus = g_test_dbus_new (G_TEST_DBUS_NONE);
+	register_dbus_service_dir (test_dbus, DATADIR);
+
+	for (ii = 0; system_data_dirs[ii] != NULL; ii++) {
+		register_dbus_service_dir (test_dbus, system_data_dirs[ii]);
+	}
+
+	return test_dbus;
+}
diff --git a/tests/libedataserver/e-test-dbus-utils.h b/tests/libedataserver/e-test-dbus-utils.h
new file mode 100644
index 0000000..8518ffc
--- /dev/null
+++ b/tests/libedataserver/e-test-dbus-utils.h
@@ -0,0 +1,33 @@
+/*
+ * e-test-dbus-utils.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifndef E_TEST_DBUS_UTILS_H
+#define E_TEST_DBUS_UTILS_H
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+const gchar *	e_test_setup_base_directories	(void);
+gboolean	e_test_clean_base_directories	(GError **error);
+
+GTestDBus *	e_test_setup_dbus_session	(void);
+
+G_END_DECLS
+
+#endif /* E_TEST_DBUS_UTILS_H */
