[evolution-data-server/openismus-phonenumber-work] sqlite: Store E.164 param in vcards



commit 7e673b05406205d324ac34f8a54b225f840f1d7e
Author: Mathias Hasselmann <mathias openismus com>
Date:   Mon Dec 10 15:24:57 2012 +0100

    sqlite: Store E.164 param in vcards

 addressbook/libebook-contacts/e-vcard.h            |    1 +
 .../libedata-book/e-book-backend-sqlitedb.c        |  260 +++++++++++++++---
 tests/libebook/client/Makefile.am                  |    4 +
 tests/libebook/client/client-test-utils.c          |   16 +
 tests/libebook/client/client-test-utils.h          |    1 +
 .../client/test-client-change-country-code.c       |  288 ++++++++++++++++++++
 6 files changed, 531 insertions(+), 39 deletions(-)
---
diff --git a/addressbook/libebook-contacts/e-vcard.h b/addressbook/libebook-contacts/e-vcard.h
index 9a39c0f..6bf72c7 100644
--- a/addressbook/libebook-contacts/e-vcard.h
+++ b/addressbook/libebook-contacts/e-vcard.h
@@ -81,6 +81,7 @@ G_BEGIN_DECLS
 #define EVC_X_DEST_EMAIL_NUM		"X-EVOLUTION-DEST-EMAIL-NUM"
 #define EVC_X_DEST_HTML_MAIL		"X-EVOLUTION-DEST-HTML-MAIL"
 #define EVC_X_DEST_SOURCE_UID		"X-EVOLUTION-DEST-SOURCE-UID"
+#define EVC_X_E164			"X-EVOLUTION-E164"
 #define EVC_X_FILE_AS			"X-EVOLUTION-FILE-AS"
 #define EVC_X_GADUGADU			"X-GADUGADU"
 #define EVC_X_GROUPWISE			"X-GROUPWISE"
diff --git a/addressbook/libedata-book/e-book-backend-sqlitedb.c b/addressbook/libedata-book/e-book-backend-sqlitedb.c
index 05171cb..79335c4 100644
--- a/addressbook/libedata-book/e-book-backend-sqlitedb.c
+++ b/addressbook/libedata-book/e-book-backend-sqlitedb.c
@@ -23,6 +23,7 @@
 #include <ctype.h>
 #include <stdlib.h>
 #include <errno.h>
+#include <langinfo.h>
 
 #include <glib/gi18n.h>
 #include <glib/gstdio.h>
@@ -40,7 +41,7 @@
 #define d(x)
 
 #define DB_FILENAME "contacts.db"
-#define FOLDER_VERSION 3
+#define FOLDER_VERSION 4
 
 #define READER_LOCK(ebsdb) g_static_rw_lock_reader_lock (&ebsdb->priv->rwlock)
 #define READER_UNLOCK(ebsdb) g_static_rw_lock_reader_unlock (&ebsdb->priv->rwlock)
@@ -75,6 +76,8 @@ struct _EBookBackendSqliteDBPrivate {
 	gint            n_summary_fields;
 	guint           have_attr_list : 1;
 	IndexFlags      attr_list_indexes;
+
+	gchar          *country_code;
 };
 
 G_DEFINE_TYPE (EBookBackendSqliteDB, e_book_backend_sqlitedb, G_TYPE_OBJECT)
@@ -120,6 +123,10 @@ static gboolean append_summary_field (GArray         *array,
 				      gboolean       *have_attr_list,
 				      GError        **error);
 
+static gboolean validate_county_code (EBookBackendSqliteDB  *ebsdb,
+                                      const gchar           *folderid,
+                                      GError               **error);
+
 static const gchar *
 summary_dbname_from_field (EBookBackendSqliteDB *ebsdb, EContactField field)
 {
@@ -207,6 +214,7 @@ e_book_backend_sqlitedb_finalize (GObject *object)
 
 	g_free (priv->path);
 	g_free (priv->summary_fields);
+	g_free (priv->country_code);
 
 	g_mutex_free (priv->in_transaction_lock);
 
@@ -473,7 +481,8 @@ create_folders_table (EBookBackendSqliteDB *ebsdb,
 		" version INTEGER,"
 		"  revision TEXT,"
 		" multivalues TEXT,"
-		"  reverse_multivalues INTEGER )";
+		"  reverse_multivalues INTEGER,"
+		" countrycode VARCHAR(2) )";
 
 	if (!book_backend_sqlitedb_start_transaction (ebsdb, error))
 		return;
@@ -513,7 +522,6 @@ create_folders_table (EBookBackendSqliteDB *ebsdb,
 	/* Upgrade DB to version 3, add multivalues introspection columns
 	 */
 	if (!err && version >= 1 && version < 3) {
-
 		stmt = "ALTER TABLE folders ADD COLUMN multivalues TEXT";
 		book_backend_sql_exec (
 			ebsdb->priv->db, stmt, NULL, NULL, &err);
@@ -523,6 +531,14 @@ create_folders_table (EBookBackendSqliteDB *ebsdb,
 			ebsdb->priv->db, stmt, NULL, NULL, &err);
 	}
 
+	/* Upgrade DB to version 4, add country-code column
+	 */
+	if (!err && version >= 1 && version < 4) {
+		stmt = "ALTER TABLE folders ADD COLUMN countrycode VARCHAR(2)";
+		book_backend_sql_exec (
+			ebsdb->priv->db, stmt, NULL, NULL, &err);
+	}
+
 	if (!err && version >= 1 && version < FOLDER_VERSION) {
 		gchar *version_update_stmt =
 			sqlite3_mprintf ("UPDATE folders SET version = %d", FOLDER_VERSION);
@@ -596,9 +612,9 @@ add_folder_into_db (EBookBackendSqliteDB *ebsdb,
 
 	stmt = sqlite3_mprintf (
 		"INSERT OR IGNORE INTO folders VALUES "
-		"( %Q, %Q, %Q, %d, %d, %d, %Q, %Q, %d ) ",
+		"( %Q, %Q, %Q, %d, %d, %d, %Q, %Q, %d, %Q ) ",
 		folderid, folder_name, NULL, 0, 0, FOLDER_VERSION,
-		NULL, multivalues, computed);
+		NULL, multivalues, computed, NULL);
 	book_backend_sql_exec (
 		ebsdb->priv->db, stmt, NULL, NULL, error);
 	sqlite3_free (stmt);
@@ -764,6 +780,16 @@ introspect_summary (EBookBackendSqliteDB *ebsdb,
 		}
 	}
 
+	/* Retreive the country code used for guessing phone numbers */
+	stmt = sqlite3_mprintf (
+		"SELECT countrycode FROM folders WHERE folder_id = %Q", folderid);
+	success = book_backend_sql_exec (
+		ebsdb->priv->db, stmt, get_string_cb, &ebsdb->priv->country_code, error);
+	sqlite3_free (stmt);
+
+	if (!success)
+		goto introspect_summary_finish;
+
  introspect_summary_finish:
 
 	g_list_free_full (summary_columns, (GDestroyNotify)g_free);
@@ -921,6 +947,9 @@ create_contacts_table (EBookBackendSqliteDB *ebsdb,
 	if (!err)
 		ret = introspect_summary (ebsdb, folderid, &err);
 
+	if (!err)
+		ret = validate_county_code (ebsdb, folderid, &err);
+
 	if (err)
 		g_propagate_error (error, err);
 
@@ -1344,13 +1373,14 @@ mprintf_suffix (const gchar *normal)
 }
 
 static gchar *
-convert_phone (const gchar *normal)
+convert_phone (const gchar *normal,
+               const gchar *country_code)
 {
 	EPhoneNumber *number = NULL;
 	gchar *phone_number = NULL;
 
 	if (normal)
-		number = e_phone_number_from_string (normal, NULL, NULL);
+		number = e_phone_number_from_string (normal, country_code, NULL);
 
 	if (number) {
 		phone_number = e_phone_number_to_string (number, E_PHONE_NUMBER_FORMAT_E164);
@@ -1361,9 +1391,10 @@ convert_phone (const gchar *normal)
 }
 
 static gchar *
-mprintf_phone (const gchar *normal)
+mprintf_phone (const gchar *normal,
+               const gchar *country_code)
 {
-	gchar *phone = convert_phone (normal);
+	gchar *phone = convert_phone (normal, country_code);
 	gchar *stmt = NULL;
 
 	if (phone) {
@@ -1374,11 +1405,81 @@ mprintf_phone (const gchar *normal)
 	return stmt;
 }
 
+static EVCardAttributeParam *
+find_param (EVCardAttribute *attr,
+            const gchar     *name)
+{
+	GList *l;
+
+	for (l = e_vcard_attribute_get_params (attr); l; l = l->next) {
+		EVCardAttributeParam *const param = l->data;
+
+		if (strcmp (e_vcard_attribute_param_get_name (param), name) == 0)
+			return param;
+	}
+
+	return NULL;
+}
+
+static gboolean
+update_e164_params (EVCard *vcard,
+                    const gchar *country_code)
+{
+	const GList *attr_list = e_vcard_get_attributes (vcard);
+	gboolean modified = FALSE;
+
+	if (!e_phone_number_is_supported ())
+		return FALSE;
+
+	for (; attr_list; attr_list = attr_list->next) {
+		EVCardAttribute *const attr = attr_list->data;
+		char *normalized_number = NULL;
+		char *formatted_number = NULL;
+		EVCardAttributeParam *param = NULL;
+
+		/* Skip all attributes but phone numbers. */
+		if (strcmp (e_vcard_attribute_get_name (attr), EVC_TEL) != 0)
+			continue;
+
+		/* Compute normalized phone number. */
+		param = find_param (attr, EVC_X_E164);
+		formatted_number = e_vcard_attribute_get_value (attr);
+
+		if (formatted_number)
+			normalized_number = convert_phone (formatted_number, country_code);
+
+		/* Update the phone number attribute. */
+		if (normalized_number) {
+			if (param == NULL) {
+				param = e_vcard_attribute_param_new (EVC_X_E164);
+				e_vcard_attribute_add_param_with_value (attr, param, normalized_number);
+				modified = TRUE;
+			} else {
+				GList *values = e_vcard_attribute_param_get_values (param);
+
+				if (values == NULL
+					|| g_strcmp0 (values->data, normalized_number)
+					|| values->next) {
+					e_vcard_attribute_param_remove_values (param);
+					e_vcard_attribute_param_add_value (param, normalized_number);
+					modified = TRUE;
+				}
+			}
+
+			g_free (normalized_number);
+		} else if (param) {
+			e_vcard_attribute_remove_param (attr, EVC_X_E164);
+			modified = TRUE;
+		}
+	}
+
+	return modified;
+}
+
 /* Add Contact (free the result with g_free() ) */
 static gchar *
 insert_stmt_from_contact (EBookBackendSqliteDB *ebsdb,
-			  EContact *contact,
-                          gboolean partial_content,
+                          EContact *contact,
                           const gchar *folderid,
                           gboolean store_vcard)
 {
@@ -1420,7 +1521,7 @@ insert_stmt_from_contact (EBookBackendSqliteDB *ebsdb,
 			}
 
 			if ((ebsdb->priv->summary_fields[i].index & INDEX_PHONE) != 0) {
-				str = mprintf_phone (normal);
+				str = mprintf_phone (normal, ebsdb->priv->country_code);
 				g_string_append (string, ", ");
 				g_string_append (string, str);
 				sqlite3_free (str);
@@ -1442,8 +1543,14 @@ insert_stmt_from_contact (EBookBackendSqliteDB *ebsdb,
 			g_warn_if_reached ();
 	}
 
-	vcard_str = store_vcard ? e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30) : NULL;
-	str       = sqlite3_mprintf (", %Q, %Q)", vcard_str, NULL);
+	if (store_vcard) {
+		update_e164_params (E_VCARD (contact), ebsdb->priv->country_code);
+		vcard_str = e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30);
+	} else {
+		vcard_str = NULL;
+	}
+
+	str = sqlite3_mprintf (", %Q, %Q)", vcard_str, NULL);
 
 	g_string_append (string, str);
 
@@ -1453,25 +1560,25 @@ insert_stmt_from_contact (EBookBackendSqliteDB *ebsdb,
 	return g_string_free (string, FALSE);
 }
 
-static void
+static gboolean
 insert_contact (EBookBackendSqliteDB *ebsdb,
 		EContact *contact,
-		gboolean partial_content,
 		const gchar *folderid,
 		GError **error)
 {
 	EBookBackendSqliteDBPrivate *priv;
+	gboolean success;
 	gchar *stmt;
 
 	priv = ebsdb->priv;
 
 	/* Update main summary table */
-	stmt = insert_stmt_from_contact (ebsdb, contact, partial_content, folderid, priv->store_vcard);
-	book_backend_sql_exec (priv->db, stmt, NULL, NULL, error);
+	stmt = insert_stmt_from_contact (ebsdb, contact, folderid, priv->store_vcard);
+	success = book_backend_sql_exec (priv->db, stmt, NULL, NULL, error);
 	g_free (stmt);
 
 	/* Update attribute list table */
-	if ((!error || (*error == NULL)) && priv->have_attr_list) {
+	if (success && priv->have_attr_list) {
 		gchar *list_folder = g_strdup_printf ("%s_lists", folderid);
 		gchar *uid;
 		gint   i;
@@ -1480,17 +1587,16 @@ insert_contact (EBookBackendSqliteDB *ebsdb,
 		/* First remove all entries for this UID */
 		uid = e_contact_get (contact, E_CONTACT_UID);
 		stmt = sqlite3_mprintf ("DELETE FROM %Q WHERE uid = %Q", list_folder, uid);
-		book_backend_sql_exec (priv->db, stmt, NULL, NULL, error);
+		success = book_backend_sql_exec (priv->db, stmt, NULL, NULL, error);
 		sqlite3_free (stmt);
 
-		for (i = 0; (!error || (*error == NULL)) && i < priv->n_summary_fields; i++) {
-
+		for (i = 0; success && i < priv->n_summary_fields; i++) {
 			if (priv->summary_fields[i].type != E_TYPE_CONTACT_ATTR_LIST)
 				continue;
 
 			values = e_contact_get (contact, priv->summary_fields[i].field);
 
-			for (l = values; (!error || (*error == NULL)) && l != NULL; l = l->next) {
+			for (l = values; success && l != NULL; l = l->next) {
 				gchar *value = (gchar *)l->data;
 				gchar *normal = e_util_utf8_normalize (value);
 				gchar *stmt_suffix = NULL;
@@ -1499,7 +1605,7 @@ insert_contact (EBookBackendSqliteDB *ebsdb,
 				if ((priv->attr_list_indexes & INDEX_SUFFIX) != 0)
 					stmt_suffix = mprintf_suffix (normal);
 				if ((priv->attr_list_indexes & INDEX_PHONE) != 0)
-					stmt_phone = mprintf_phone (normal);
+					stmt_phone = mprintf_phone (normal, ebsdb->priv->country_code);
 
 				stmt = sqlite3_mprintf ("INSERT INTO %Q (uid, field, value%s%s) "
 							"VALUES (%Q, %Q, %Q%s%s%s%s)",
@@ -1517,7 +1623,7 @@ insert_contact (EBookBackendSqliteDB *ebsdb,
 				if (stmt_phone)
 					sqlite3_free (stmt_phone);
 
-				book_backend_sql_exec (priv->db, stmt, NULL, NULL, error);
+				success = book_backend_sql_exec (priv->db, stmt, NULL, NULL, error);
 				sqlite3_free (stmt);
 				g_free (normal);
 			}
@@ -1529,6 +1635,8 @@ insert_contact (EBookBackendSqliteDB *ebsdb,
 		g_free (list_folder);
 		g_free (uid);
 	}
+
+	return success;
 }
 
 
@@ -1603,7 +1711,7 @@ e_book_backend_sqlitedb_add_contacts (EBookBackendSqliteDB *ebsdb,
 	for (l = contacts; !err && l != NULL; l = g_slist_next (l)) {
 		EContact *contact = (EContact *) l->data;
 
-		insert_contact (ebsdb, contact, partial_content, folderid, &err);
+		insert_contact (ebsdb, contact, folderid, &err);
 	}
 
 	book_backend_sqlitedb_end_transaction (ebsdb, !err, err ? NULL : &err);
@@ -2211,12 +2319,14 @@ func_check_phone (struct _ESExp         *f,
                   struct _ESExpResult **argv,
                   gpointer              data)
 {
+	EBookBackendSqliteDB *ebsdb = data;
 	ESExpResult *const r = func_check (f, argc, argv, data);
 
 	if (r && r->value.number) {
 		GError *error = NULL;
-		const gchar *query_value = argv[1]->value.string;
-		EPhoneNumber *number = e_phone_number_from_string (query_value, NULL, &error);
+		const gchar *const query_value = argv[1]->value.string;
+		EPhoneNumber *const number = e_phone_number_from_string (
+				query_value, ebsdb->priv->country_code, &error);
 
 		if (number == NULL) {
 			if (error) {
@@ -2413,9 +2523,10 @@ typedef enum {
 } ConvertFlags;
 
 static gchar *
-convert_string_value (const gchar *value,
-                      ConvertFlags flags,
-                      MatchType match)
+convert_string_value (EBookBackendSqliteDB *ebsdb,
+                      const gchar          *value,
+                      ConvertFlags          flags,
+                      MatchType             match)
 {
 	GString *str;
 	size_t len;
@@ -2457,7 +2568,7 @@ convert_string_value (const gchar *value,
 		computed = g_utf8_strreverse (normal, -1);
 		ptr = computed;
 	} else if (flags & CONVERT_PHONE) {
-		computed = convert_phone (normal);
+		computed = convert_phone (normal, ebsdb->priv->country_code);
 		ptr = computed;
 	} else {
 		ptr = normal;
@@ -2516,7 +2627,7 @@ field_name_and_query_term (EBookBackendSqliteDB *ebsdb,
 	if (summary_index < 0) {
 		g_critical ("Only summary field matches should be converted to sql queries");
 		field_name = g_strconcat (folderid, ".", field_name_input, NULL);
-		value = convert_string_value (query_term_input, CONVERT_NORMALIZE, match);
+		value = convert_string_value (ebsdb, query_term_input, CONVERT_NORMALIZE, match);
 	} else {
 		gboolean suffix_search = FALSE;
 		gboolean phone_search = FALSE;
@@ -2554,10 +2665,10 @@ field_name_and_query_term (EBookBackendSqliteDB *ebsdb,
 
 			if (ebsdb->priv->summary_fields[summary_index].field == E_CONTACT_UID ||
 			    ebsdb->priv->summary_fields[summary_index].field == E_CONTACT_REV)
-				value = convert_string_value (query_term_input, CONVERT_REVERSE,
+				value = convert_string_value (ebsdb, query_term_input, CONVERT_REVERSE,
 							      (match == MATCH_ENDS_WITH) ? MATCH_BEGINS_WITH : MATCH_IS);
 			else
-				value = convert_string_value (query_term_input, CONVERT_REVERSE | CONVERT_NORMALIZE,
+				value = convert_string_value (ebsdb, query_term_input, CONVERT_REVERSE | CONVERT_NORMALIZE,
 							      (match == MATCH_ENDS_WITH) ? MATCH_BEGINS_WITH : MATCH_IS);
 		} else if (phone_search) {
 			/* Special case for E.164 matching:
@@ -2574,10 +2685,10 @@ field_name_and_query_term (EBookBackendSqliteDB *ebsdb,
 
 			if (ebsdb->priv->summary_fields[summary_index].field == E_CONTACT_UID ||
 			    ebsdb->priv->summary_fields[summary_index].field == E_CONTACT_REV)
-				value = convert_string_value (query_term_input, CONVERT_PHONE,
+				value = convert_string_value (ebsdb, query_term_input, CONVERT_PHONE,
 							      (match == MATCH_ENDS_WITH) ? MATCH_BEGINS_WITH : MATCH_IS);
 			else
-				value = convert_string_value (query_term_input, CONVERT_PHONE | CONVERT_NORMALIZE,
+				value = convert_string_value (ebsdb, query_term_input, CONVERT_PHONE | CONVERT_NORMALIZE,
 							      (match == MATCH_ENDS_WITH) ? MATCH_BEGINS_WITH : MATCH_IS);
 		} else {
 
@@ -2590,9 +2701,9 @@ field_name_and_query_term (EBookBackendSqliteDB *ebsdb,
 
 			if (ebsdb->priv->summary_fields[summary_index].field == E_CONTACT_UID ||
 			    ebsdb->priv->summary_fields[summary_index].field == E_CONTACT_REV)
-				value = convert_string_value (query_term_input, CONVERT_NOTHING, match);
+				value = convert_string_value (ebsdb, query_term_input, CONVERT_NOTHING, match);
 			else
-				value = convert_string_value (query_term_input, CONVERT_NORMALIZE, match);
+				value = convert_string_value (ebsdb, query_term_input, CONVERT_NORMALIZE, match);
 		}
 	}
 
@@ -3782,3 +3893,74 @@ e_book_backend_sqlitedb_remove (EBookBackendSqliteDB *ebsdb,
 
 	return TRUE;
 }
+
+static void
+destroy_search_data (gpointer data)
+{
+	e_book_backend_sqlitedb_search_data_free (data);
+}
+
+static gboolean
+validate_county_code (EBookBackendSqliteDB  *ebsdb,
+                      const gchar           *folderid,
+                      GError               **error)
+{
+	gchar *stmt;
+	gboolean success = FALSE;
+	GSList *vcard_data = NULL;
+	GSList *l;
+
+#if HAVE__NL_ADDRESS_COUNTRY_AB2
+	const gchar *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 */
+
+	if (country_code == NULL) {
+		g_warning ("Cannot derive 2-letter country code from current locale.");
+		country_code = "ZZ";
+	}
+
+	/* Nothing has changed. We can stop here. */
+	if (g_strcmp0 (country_code, ebsdb->priv->country_code) == 0)
+		return TRUE;
+
+	g_free (ebsdb->priv->country_code);
+	ebsdb->priv->country_code = g_strdup (country_code);
+
+	g_print ("The country code has changed %s. "
+		 "Must rebuilding %s parameters and indexes for stored vCards.\n",
+	         ebsdb->priv->country_code, EVC_X_E164);
+
+	stmt = sqlite3_mprintf ("SELECT uid, vcard, NULL FROM %Q", folderid);
+	success = book_backend_sql_exec (
+		ebsdb->priv->db, stmt, addto_vcard_list_cb, &vcard_data, error);
+	sqlite3_free (stmt);
+
+	for (l = vcard_data; success && l; l = l->next) {
+		EbSdbSearchData *const s_data = l->data;
+		EContact *contact = e_contact_new_from_vcard_with_uid (s_data->vcard, s_data->uid);
+
+		if (contact == NULL)
+			continue;
+
+		if (update_e164_params (E_VCARD (contact), ebsdb->priv->country_code))
+			success = insert_contact (ebsdb, contact, folderid, error);
+
+		g_object_unref (contact);
+	}
+
+	g_slist_free_full (vcard_data, destroy_search_data);
+
+	if (success) {
+		stmt = sqlite3_mprintf (
+			"UPDATE folders SET countrycode = %Q WHERE folder_id = %Q",
+			country_code, folderid);
+		success = book_backend_sql_exec (
+			ebsdb->priv->db, stmt, get_string_cb, &ebsdb->priv->country_code, error);
+		sqlite3_free (stmt);
+	}
+
+	return success;
+}
+
diff --git a/tests/libebook/client/Makefile.am b/tests/libebook/client/Makefile.am
index 3972b86..b8f6a10 100644
--- a/tests/libebook/client/Makefile.am
+++ b/tests/libebook/client/Makefile.am
@@ -17,6 +17,7 @@ libclient_test_utils_la_CPPFLAGS =				\
 	-DBACKENDDIR=\"$(ebook_backenddir)\"			\
 	-DDATADIR=\"$(datadir)\"				\
 	-DSRCDIR=\""$(abs_srcdir)"\"				\
+	-DBUILDDIR=\""$(abs_topbuilddir)"\"			\
 	$(EVOLUTION_ADDRESSBOOK_CFLAGS)				\
 	$(CAMEL_CFLAGS)						\
 	$(NULL)
@@ -48,6 +49,7 @@ TESTS =								\
 	test-client-remove-contact-by-uid			\
 	test-client-remove-contacts				\
 	test-client-photo-is-uri				\
+	test-client-change-country-code				\
 	test-client-write-write					\
 	test-client-stress-factory--serial			\
 	test-client-stress-factory--fifo			\
@@ -116,6 +118,8 @@ test_client_remove_contacts_LDADD=$(TEST_LIBS)
 test_client_remove_contacts_CPPFLAGS=$(TEST_CPPFLAGS)
 test_client_photo_is_uri_LDADD=$(TEST_LIBS)
 test_client_photo_is_uri_CPPFLAGS=$(TEST_CPPFLAGS)
+test_client_change_country_code_LDADD=$(TEST_LIBS)
+test_client_change_country_code_CPPFLAGS=$(TEST_CPPFLAGS)
 test_client_write_write_LDADD=$(TEST_LIBS) $(top_builddir)/addressbook/libedata-book/libedata-book-1.2.la
 test_client_write_write_CPPFLAGS=$(TEST_CPPFLAGS)
 test_client_stress_factory__fifo_LDADD=$(TEST_LIBS)
diff --git a/tests/libebook/client/client-test-utils.c b/tests/libebook/client/client-test-utils.c
index a67a94b..57a7716 100644
--- a/tests/libebook/client/client-test-utils.c
+++ b/tests/libebook/client/client-test-utils.c
@@ -244,6 +244,22 @@ stop_main_loop (gint stop_result)
 	g_main_loop_quit (loop);
 }
 
+static gboolean
+sleep_in_main_loop_cb (gpointer data)
+{
+	g_main_loop_quit (data);
+	return FALSE;
+}
+
+void
+sleep_in_main_loop (guint msec)
+{
+	GMainLoop *loop = g_main_loop_new (NULL, FALSE);
+	g_timeout_add (msec, sleep_in_main_loop_cb, loop);
+	g_main_loop_run (loop);
+	g_main_loop_unref (loop);
+}
+
 /* returns value used in stop_main_loop() */
 gint
 get_main_loop_stop_result (void)
diff --git a/tests/libebook/client/client-test-utils.h b/tests/libebook/client/client-test-utils.h
index 59d9632..0b201ee 100644
--- a/tests/libebook/client/client-test-utils.h
+++ b/tests/libebook/client/client-test-utils.h
@@ -37,6 +37,7 @@ void main_initialize (void);
 void start_main_loop (GThreadFunc func, gpointer data);
 void start_in_thread_with_main_loop (GThreadFunc func, gpointer data);
 void start_in_idle_with_main_loop (GThreadFunc func, gpointer data);
+void sleep_in_main_loop (guint msec);
 void stop_main_loop (gint stop_result);
 gint get_main_loop_stop_result (void);
 
diff --git a/tests/libebook/client/test-client-change-country-code.c b/tests/libebook/client/test-client-change-country-code.c
new file mode 100644
index 0000000..0ecc7d8
--- /dev/null
+++ b/tests/libebook/client/test-client-change-country-code.c
@@ -0,0 +1,288 @@
+/* -*- 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>
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <locale.h>
+#include <langinfo.h>
+
+#include <libebook/libebook.h>
+
+#include "client-test-utils.h"
+
+
+/* This forces the GType to be registered in a way that
+ * avoids a "statement with no effect" compiler warning.
+ * FIXME Use g_type_ensure() once we require GLib 2.34. */
+#define REGISTER_TYPE(type) \
+	(g_type_class_unref (g_type_class_ref (type)))
+
+static void
+setup_custom_summary (ESource *scratch)
+{
+	ESourceBackendSummarySetup *setup;
+
+	REGISTER_TYPE (E_TYPE_SOURCE_BACKEND_SUMMARY_SETUP);
+	setup = e_source_get_extension (scratch, E_SOURCE_EXTENSION_BACKEND_SUMMARY_SETUP);
+	e_source_backend_summary_setup_set_summary_fields (setup,
+							   E_CONTACT_TEL,
+							   0);
+	e_source_backend_summary_setup_set_indexed_fields (setup,
+							   E_CONTACT_TEL, E_BOOK_INDEX_PHONE,
+							   0);
+}
+
+static gboolean
+query_service_pid (const char  *name,
+                   GPid        *pid,
+                   GError     **error)
+{
+	GDBusConnection *connection;
+	GVariant *rv;
+
+	connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, error);
+
+	if (connection == NULL)
+		return FALSE;
+
+	rv = g_dbus_connection_call_sync (connection,
+	                                  "org.freedesktop.DBus",
+	                                  "/org/freedesktop/DBus",
+	                                  "org.freedesktop.DBus",
+	                                  "GetConnectionUnixProcessID",
+	                                  g_variant_new ("(s)", name),
+	                                  NULL, G_DBUS_CALL_FLAGS_NONE, -1,
+	                                  NULL, error);
+
+	g_object_unref (connection);
+
+	if (rv == NULL)
+		return FALSE;
+
+	g_variant_get (rv, "(u)", pid);
+	g_variant_unref (rv);
+
+	return TRUE;
+}
+
+static gboolean
+spawn_addressbook_factory (GPid *pid, GError **error)
+{
+	int i;
+	GPid child_pid = 0;
+	GPid service_pid = 0;
+
+	char *argv[] = {
+		g_build_filename (BUILDDIR, "services/evolution-addressbook-factory/evolution-addressbook-factory", NULL),
+		NULL
+	};
+	
+	gboolean success = g_spawn_async (NULL, argv, NULL,
+                                          G_SPAWN_LEAVE_DESCRIPTORS_OPEN,
+                                          NULL, NULL, &child_pid, error);
+
+	g_free (argv[0]);
+
+	/* Waiting a bit for the addressbook factory starting up so that it can claim its D-Bus name */
+	for (i = 0; success && service_pid == 0 && i < 10; ++i) {
+		sleep_in_main_loop (150);
+		success = query_service_pid (ADDRESS_BOOK_DBUS_SERVICE_NAME, &service_pid, error);
+	}
+
+	if (success) {
+		g_assert_cmpuint (service_pid, ==, child_pid);
+
+		if (pid)
+			*pid = child_pid;
+	}
+
+	return success;
+}
+
+gint
+main (gint argc,
+      gchar **argv)
+{
+	EBookClient *client;
+	GError *error = NULL;
+	EContact *contact;
+	gchar *source_uid;
+	gchar *contact_uid;
+	EVCardAttribute *attr;
+	GList *e164_values;
+	gboolean success;
+	ESource *source;
+	GPid factory_pid = 0;
+	GSList *fetched_uids;
+
+	g_test_init (&argc, &argv, NULL);
+
+	/*** Setup ****/
+
+	/* Setting with U.S. locale for addresses */
+	g_setenv ("LC_ADDRESS", "en_US.UTF-8", TRUE);
+	setlocale (LC_ADDRESS, "");
+
+	g_assert_cmpstr (nl_langinfo (_NL_ADDRESS_COUNTRY_AB2), ==, "US");
+
+	/* Initializing fake D-Bus and event loop machinery */
+	main_initialize ();
+	sleep_in_main_loop (500);
+
+	/* Launching addressbook factory on fake-dbus */
+	success = spawn_addressbook_factory (&factory_pid, &error);
+	g_assert_cmpstr (error ? error->message : NULL, ==, NULL);
+	g_assert_cmpuint (factory_pid, !=, 0);
+	g_assert (success);
+
+	/* Creating the test addressbook */
+	client = new_custom_temp_client (&source_uid, setup_custom_summary);
+	g_assert (client != NULL);
+
+	success = e_client_open_sync (E_CLIENT (client), FALSE, NULL, &error);
+	g_assert_cmpstr (error ? error->message : NULL, ==, NULL);
+	g_assert (success);
+
+	/* Add test contact */
+	contact = e_contact_new_from_vcard ("BEGIN:VCARD\nTEL:221.542.3789\nEND:VCARD");
+	success = e_book_client_add_contact_sync (client, contact, &contact_uid, NULL, &error);
+	g_assert_cmpstr (error ? error->message : NULL, ==, NULL);
+	g_assert (success);
+	g_object_unref (contact);
+
+	/*** Verification of U.S. context */
+
+	/* Fetch the contact by UID and check EVC_TEL attribute and its EVC_X_E164 parameter */
+	success = e_book_client_get_contact_sync (client, contact_uid, &contact, NULL, &error);
+	g_assert_cmpstr (error ? error->message : NULL, ==, NULL);
+	g_assert (success);
+
+	attr = e_vcard_get_attribute (E_VCARD (contact), EVC_TEL);
+
+	g_assert (attr != NULL);
+	g_assert_cmpstr (e_vcard_attribute_get_value (attr), ==, "221.542.3789");
+
+	e164_values = e_vcard_attribute_get_param (attr, EVC_X_E164);
+
+	g_assert (e164_values != NULL);
+	g_assert_cmpstr (e164_values->data, ==, "+12215423789");
+
+	/* Now resolve the contact via its phone number, assuming indexes are used */
+	success = e_book_client_get_contacts_uids_sync (
+			client, "(eqphone \"phone\" \"+1/221/5423789\")",
+			&fetched_uids, NULL, &error);
+	g_assert_cmpstr (error ? error->message : NULL, ==, NULL);
+	g_assert (success);
+
+	g_assert_cmpuint (g_slist_length (fetched_uids), ==, 1);
+	g_assert_cmpstr (fetched_uids->data, ==, contact_uid);
+	g_slist_free_full (fetched_uids, g_free);
+
+	success = e_book_client_get_contacts_uids_sync (
+			client, "(eqphone \"phone\" \"+49 (221) 5423789\")",
+			&fetched_uids, NULL, &error);
+	g_assert_cmpstr (error ? error->message : NULL, ==, NULL);
+	g_assert (success);
+
+	g_assert_cmpuint (g_slist_length (fetched_uids), ==, 0);
+	g_slist_free_full (fetched_uids, g_free);
+
+	/*** Switch to German locale */
+
+	/* Shutdown address book factory on fake D-Bus */
+	g_object_unref (client);
+
+	success = (kill (factory_pid, SIGTERM) == 0);
+	g_assert (success);
+	factory_pid = 0;
+
+	/* Wait a bit to let GDBus notice what happened... */
+	sleep_in_main_loop (1500);
+
+	/* Switch to German locale */
+	g_setenv ("LC_ADDRESS", "de_DE.UTF-8", TRUE);
+	setlocale (LC_ADDRESS, "");
+
+	g_assert_cmpstr (nl_langinfo (_NL_ADDRESS_COUNTRY_AB2), ==, "DE");
+
+	/* Respawn the addressbook factory */
+	success = spawn_addressbook_factory (&factory_pid, &error);
+	g_assert_cmpstr (error ? error->message : NULL, ==, NULL);
+	g_assert_cmpuint (factory_pid, !=, 0);
+	g_assert (success);
+
+	/* Reopen the book */
+	source = e_source_new_with_uid (source_uid, NULL, &error);
+	g_assert_cmpstr (error ? error->message : NULL, ==, NULL);
+	g_assert (source != NULL);
+
+	client = e_book_client_new (source, &error);
+	g_assert_cmpstr (error ? error->message : NULL, ==, NULL);
+	g_assert (client != NULL);
+
+	success = e_client_open_sync (E_CLIENT (client), FALSE, NULL, &error);
+	g_assert_cmpstr (error ? error->message : NULL, ==, NULL);
+	g_assert (success);
+
+	/*** Verification of German context */
+
+	/* Fetch the contact by UID and check EVC_TEL attribute and its EVC_X_E164 parameter */
+	success = e_book_client_get_contact_sync (client, contact_uid, &contact, NULL, &error);
+	g_assert_cmpstr (error ? error->message : NULL, ==, NULL);
+	g_assert (success);
+
+	attr = e_vcard_get_attribute (E_VCARD (contact), EVC_TEL);
+
+	g_assert (attr != NULL);
+	g_assert_cmpstr (e_vcard_attribute_get_value (attr), ==, "221.542.3789");
+
+	e164_values = e_vcard_attribute_get_param (attr, EVC_X_E164);
+
+	g_assert (e164_values != NULL);
+	g_assert_cmpstr (e164_values->data, ==, "+492215423789");
+
+	/* Now resolve the contact via its phone number, assuming indexes are used */
+	success = e_book_client_get_contacts_uids_sync (
+			client, "(eqphone \"phone\" \"+1/221/5423789\")",
+			&fetched_uids, NULL, &error);
+	g_assert_cmpstr (error ? error->message : NULL, ==, NULL);
+	g_assert (success);
+
+	g_assert_cmpuint (g_slist_length (fetched_uids), ==, 0);
+	g_slist_free_full (fetched_uids, g_free);
+
+	success = e_book_client_get_contacts_uids_sync (
+			client, "(eqphone \"phone\" \"+49 (221) 5423789\")",
+			&fetched_uids, NULL, &error);
+	g_assert_cmpstr (error ? error->message : NULL, ==, NULL);
+	g_assert (success);
+
+	g_assert_cmpuint (g_slist_length (fetched_uids), ==, 1);
+	g_assert_cmpstr (fetched_uids->data, ==, contact_uid);
+	g_slist_free_full (fetched_uids, g_free);
+
+	/* Cleanup */
+	g_object_unref (source);
+	g_object_unref (client);
+
+	return g_test_run ();
+}



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